Merge "Send broadcast message when default messaging application changed" into sc-dev
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 130c775..2535365 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -536,8 +536,9 @@
// Start tracking Binder latency for the phone process.
mBinderCallsSettingsObserver = new BinderCallsStats.SettingsObserver(
getApplicationContext(),
- new BinderCallsStats(new BinderCallsStats.Injector()),
- com.android.internal.os.BinderLatencyProto.Dims.TELEPHONY);
+ new BinderCallsStats(
+ new BinderCallsStats.Injector(),
+ com.android.internal.os.BinderLatencyProto.Dims.TELEPHONY));
}
/**
diff --git a/src/com/android/phone/ServiceStateProvider.java b/src/com/android/phone/ServiceStateProvider.java
index 32562fa..08f0907 100644
--- a/src/com/android/phone/ServiceStateProvider.java
+++ b/src/com/android/phone/ServiceStateProvider.java
@@ -16,6 +16,7 @@
package com.android.phone;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.provider.Telephony.ServiceStateTable;
import static android.provider.Telephony.ServiceStateTable.CONTENT_URI;
import static android.provider.Telephony.ServiceStateTable.DATA_NETWORK_TYPE;
@@ -26,6 +27,7 @@
import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId;
import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionIdAndField;
+import android.Manifest;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
@@ -33,16 +35,21 @@
import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
import android.os.Parcel;
+import android.telephony.LocationAccessPolicy;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.TelephonyPermissions;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* The class to provide base facility to access ServiceState related content,
@@ -223,7 +230,9 @@
public static final String OPERATOR_ALPHA_SHORT_RAW = "operator_alpha_short_raw";
private final HashMap<Integer, ServiceState> mServiceStates = new HashMap<>();
- private static final String[] sColumns = {
+
+ @VisibleForTesting
+ /* package */ static final String[] ALL_COLUMNS = {
VOICE_REG_STATE,
DATA_REG_STATE,
VOICE_ROAMING_TYPE,
@@ -252,6 +261,34 @@
DUPLEX_MODE,
};
+ /**
+ * Columns that are exposed to public surface.
+ * These are the columns accessible to apps target S+ and lack
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} permission.
+ */
+ @VisibleForTesting
+ /* package */ static final String[] PUBLIC_COLUMNS = {
+ VOICE_REG_STATE,
+ DATA_REG_STATE,
+ VOICE_OPERATOR_NUMERIC,
+ IS_MANUAL_NETWORK_SELECTION,
+ DATA_NETWORK_TYPE,
+ DUPLEX_MODE
+ };
+
+ /**
+ * Columns protected by location permissions (either FINE or COARSE).
+ * SecurityException will throw if applications without location permissions try to put those
+ * columns explicitly into cursor (e.g. through {@code projection} parameter in
+ * {@link #query(Uri, String[], String, String[], String)} method).
+ * Default (scrub-out) value will return if applications try to put all columns into cursor by
+ * specifying null of {@code projection} parameter and get values through the returned cursor.
+ */
+ private static final Set<String> LOCATION_PROTECTED_COLUMNS_SET = Set.of(
+ NETWORK_ID,
+ SYSTEM_ID
+ );
+
@Override
public boolean onCreate() {
return true;
@@ -354,12 +391,55 @@
}
// Get the service state
- ServiceState ss = getServiceState(subId);
- if (ss == null) {
+ ServiceState unredactedServiceState = getServiceState(subId);
+ if (unredactedServiceState == null) {
Log.d(TAG, "returning null");
return null;
}
+ final boolean targetingAtLeastS = TelephonyPermissions.getTargetSdk(getContext(),
+ getCallingPackage()) >= Build.VERSION_CODES.S;
+ final boolean canReadPrivilegedPhoneState = getContext().checkCallingOrSelfPermission(
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE) == PERMISSION_GRANTED;
+
+ final String[] availableColumns;
+ final ServiceState ss;
+ if (targetingAtLeastS && !canReadPrivilegedPhoneState) {
+ // targetSdkVersion S+ without read privileged phone state permission can only
+ // access public columns which have no location sensitive info.
+ availableColumns = PUBLIC_COLUMNS;
+ ss = unredactedServiceState;
+ } else {
+ availableColumns = ALL_COLUMNS;
+
+ final boolean hasLocationPermission =
+ hasFineLocationPermission() || hasCoarseLocationPermission();
+ if (hasLocationPermission) {
+ ss = unredactedServiceState;
+ } else {
+ // The caller has no location permission but explicitly requires for location
+ // protected columns. Throw SecurityException to fail loudly.
+ if (projection != null) {
+ for (String requiredColumn : projection) {
+ if (LOCATION_PROTECTED_COLUMNS_SET.contains(requiredColumn)) {
+ throw new SecurityException("Column " + requiredColumn
+ + "requires location permissions to access.");
+ }
+ }
+ }
+
+ // The caller has no location permission but only requires columns without
+ // location sensitive info or "all" columns, return result that scrub out all
+ // sensitive info. In later case, we will not know which columns will be fetched
+ // from the returned cursor until the result has been returned.
+ ss = unredactedServiceState.createLocationInfoSanitizedCopy(
+ true /*removeCoarseLocation*/);
+ // TODO(b/188061647): remove the additional redaction once it is fixed in SS
+ ss.setCdmaSystemAndNetworkId(ServiceState.UNKNOWN_ID,
+ ServiceState.UNKNOWN_ID);
+ }
+ }
+
// Build the result
final int voice_reg_state = ss.getState();
final int data_reg_state = ss.getDataRegistrationState();
@@ -388,7 +468,8 @@
final int data_network_type = ss.getDataNetworkType();
final int duplex_mode = ss.getDuplexMode();
- return buildSingleRowResult(projection, sColumns, new Object[] {
+ Object[] data = availableColumns == ALL_COLUMNS ? new Object[]{
+ // data for all columns
voice_reg_state,
data_reg_state,
voice_roaming_type,
@@ -415,7 +496,17 @@
operator_alpha_short_raw,
data_network_type,
duplex_mode,
- });
+ } : new Object[]{
+ // data for public columns only
+ voice_reg_state,
+ data_reg_state,
+ voice_operator_numeric,
+ is_manual_network_selection,
+ data_network_type,
+ duplex_mode,
+ };
+
+ return buildSingleRowResult(projection, availableColumns, data);
}
}
@@ -569,4 +660,38 @@
values.put(SERVICE_STATE, p.marshall());
return values;
}
+
+ private boolean hasFineLocationPermission() {
+ LocationAccessPolicy.LocationPermissionResult fineLocationResult =
+ LocationAccessPolicy.checkLocationPermission(getContext(),
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(getCallingPackage())
+ .setCallingFeatureId(getCallingAttributionTag())
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid())
+ .setMethod("ServiceStateProvider#query")
+ .setLogAsInfo(true)
+ .setMinSdkVersionForFine(Build.VERSION_CODES.S)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.S)
+ .setMinSdkVersionForEnforcement(Build.VERSION_CODES.S)
+ .build());
+ return fineLocationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+ }
+
+ private boolean hasCoarseLocationPermission() {
+ LocationAccessPolicy.LocationPermissionResult coarseLocationResult =
+ LocationAccessPolicy.checkLocationPermission(getContext(),
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(getCallingPackage())
+ .setCallingFeatureId(getCallingAttributionTag())
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid())
+ .setMethod("ServiceStateProvider#query")
+ .setLogAsInfo(true)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.S)
+ .setMinSdkVersionForFine(Integer.MAX_VALUE)
+ .setMinSdkVersionForEnforcement(Build.VERSION_CODES.S)
+ .build());
+ return coarseLocationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+ }
}
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 2383981..cdbe8c7 100755
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -157,10 +157,11 @@
updateState();
break;
case MSG_HANDOVER_STATE_CHANGED:
+ // fall through
case MSG_REDIAL_CONNECTION_CHANGED:
String what = (msg.what == MSG_HANDOVER_STATE_CHANGED)
? "MSG_HANDOVER_STATE_CHANGED" : "MSG_REDIAL_CONNECTION_CHANGED";
- Log.v(TelephonyConnection.this, what);
+ Log.i(TelephonyConnection.this, "Connection changed due to: %s", what);
AsyncResult ar = (AsyncResult) msg.obj;
com.android.internal.telephony.Connection connection =
(com.android.internal.telephony.Connection) ar.result;
@@ -177,7 +178,7 @@
mOriginalConnection.getAddress() != null &&
mOriginalConnection.getAddress().equals(connection.getAddress())) ||
connection.getState() == mOriginalConnection.getStateBeforeHandover())) {
- Log.d(TelephonyConnection.this, "Setting original connection after"
+ Log.i(TelephonyConnection.this, "Setting original connection after"
+ " handover or redial, current original connection="
+ mOriginalConnection.toString()
+ ", new original connection="
@@ -744,6 +745,8 @@
@Override
public void onOriginalConnectionReplaced(
com.android.internal.telephony.Connection newConnection) {
+ Log.i(TelephonyConnection.this, "onOriginalConnectionReplaced; newConn=%s",
+ newConnection);
setOriginalConnection(newConnection);
}
@@ -1511,6 +1514,7 @@
}
public void registerForCallEvents(Phone phone) {
+ Log.i(this, "registerForCallEvents; phone=%s", phone);
phone.registerForPreciseCallStateChanged(mHandler, MSG_PRECISE_CALL_STATE_CHANGED, null);
phone.registerForHandoverStateChanged(mHandler, MSG_HANDOVER_STATE_CHANGED, null);
phone.registerForRedialConnectionChanged(mHandler, MSG_REDIAL_CONNECTION_CHANGED, null);
@@ -1522,7 +1526,8 @@
}
void setOriginalConnection(com.android.internal.telephony.Connection originalConnection) {
- Log.v(this, "new TelephonyConnection, originalConnection: " + originalConnection);
+ Log.i(this, "setOriginalConnection: TelephonyConnection, originalConnection: "
+ + originalConnection);
if (mOriginalConnection != null && originalConnection != null
&& !originalConnection.isIncoming()
&& originalConnection.getOrigDialString() == null
@@ -2076,6 +2081,7 @@
*/
void clearOriginalConnection() {
if (mOriginalConnection != null) {
+ Log.i(this, "clearOriginalConnection; clearing=%s", mOriginalConnection);
if (getPhone() != null) {
unregisterForCallEvents(getPhone());
}
diff --git a/tests/src/com/android/phone/ServiceStateProviderTest.java b/tests/src/com/android/phone/ServiceStateProviderTest.java
index d85976a..76d6ecc 100644
--- a/tests/src/com/android/phone/ServiceStateProviderTest.java
+++ b/tests/src/com/android/phone/ServiceStateProviderTest.java
@@ -16,22 +16,44 @@
package com.android.phone;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.provider.Telephony.ServiceStateTable;
+import static android.provider.Telephony.ServiceStateTable.DATA_NETWORK_TYPE;
+import static android.provider.Telephony.ServiceStateTable.DATA_REG_STATE;
+import static android.provider.Telephony.ServiceStateTable.DUPLEX_MODE;
+import static android.provider.Telephony.ServiceStateTable.VOICE_OPERATOR_NUMERIC;
+import static android.provider.Telephony.ServiceStateTable.VOICE_REG_STATE;
import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId;
import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
+import static com.android.phone.ServiceStateProvider.DATA_ROAMING_TYPE;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import android.Manifest;
+import android.app.AppOpsManager;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.database.ContentObserver;
import android.database.Cursor;
+import android.location.LocationManager;
import android.net.Uri;
+import android.os.Build;
+import android.os.UserHandle;
import android.telephony.AccessNetworkConstants;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
@@ -40,8 +62,13 @@
import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
/**
* Tests for simple queries of ServiceStateProvider.
@@ -53,41 +80,18 @@
* runtest --path tests/src/com/android/phone/ServiceStateProviderTest.java \
* --test-method testGetServiceState
*/
+@RunWith(AndroidJUnit4.class)
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,
- ServiceStateProvider.VOICE_OPERATOR_ALPHA_LONG,
- ServiceStateProvider.VOICE_OPERATOR_ALPHA_SHORT,
- ServiceStateTable.VOICE_OPERATOR_NUMERIC,
- ServiceStateProvider.DATA_OPERATOR_ALPHA_LONG,
- ServiceStateProvider.DATA_OPERATOR_ALPHA_SHORT,
- ServiceStateProvider.DATA_OPERATOR_NUMERIC,
- ServiceStateTable.IS_MANUAL_NETWORK_SELECTION,
- ServiceStateProvider.RIL_VOICE_RADIO_TECHNOLOGY,
- ServiceStateProvider.RIL_DATA_RADIO_TECHNOLOGY,
- ServiceStateProvider.CSS_INDICATOR,
- ServiceStateProvider.NETWORK_ID,
- ServiceStateProvider.SYSTEM_ID,
- ServiceStateProvider.CDMA_ROAMING_INDICATOR,
- ServiceStateProvider.CDMA_DEFAULT_ROAMING_INDICATOR,
- ServiceStateProvider.CDMA_ERI_ICON_INDEX,
- ServiceStateProvider.CDMA_ERI_ICON_MODE,
- ServiceStateProvider.IS_EMERGENCY_ONLY,
- ServiceStateProvider.IS_USING_CARRIER_AGGREGATION,
- ServiceStateProvider.OPERATOR_ALPHA_LONG_RAW,
- ServiceStateProvider.OPERATOR_ALPHA_SHORT_RAW,
- ServiceStateTable.DATA_NETWORK_TYPE,
- ServiceStateTable.DUPLEX_MODE,
- };
+ @Mock Context mContext;
+ @Mock AppOpsManager mAppOpsManager;
+ @Mock LocationManager mLocationManager;
+ @Mock PackageManager mPackageManager;
// Exception used internally to verify if the Resolver#notifyChange has been called.
private class TestNotifierException extends RuntimeException {
@@ -98,7 +102,11 @@
@Before
public void setUp() throws Exception {
- mContext = mock(Context.class);
+ MockitoAnnotations.initMocks(this);
+ mockSystemService(AppOpsManager.class, mAppOpsManager, Context.APP_OPS_SERVICE);
+ mockSystemService(LocationManager.class, mLocationManager, Context.LOCATION_SERVICE);
+ doReturn(mPackageManager).when(mContext).getPackageManager();
+
mContentResolver = new MockContentResolver() {
@Override
public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
@@ -141,62 +149,165 @@
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);
+ // By default, test with app target R, no READ_PRIVILEGED_PHONE_STATE permission
+ setTargetSdkVersion(Build.VERSION_CODES.R);
+ setCanReadPrivilegedPhoneState(false);
}
/**
- * Test querying the service state for a given subId
+ * 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.
*/
@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);
+ public void testQueryServiceState_withNoSubId_withoutLocation() {
+ setLocationPermissions(false);
+
+ verifyServiceStateForSubId(ServiceStateTable.CONTENT_URI, mTestServiceState,
+ false /*hasLocation*/);
}
- private void verifyServiceStateForSubId(Uri uri, ServiceState ss) {
- Cursor cursor = mContentResolver.query(uri, mTestProjection, "",
+ @Test
+ @SmallTest
+ public void testQueryServiceState_withNoSubId_withLocation() {
+ setLocationPermissions(true);
+
+ verifyServiceStateForSubId(ServiceStateTable.CONTENT_URI, mTestServiceState,
+ true /*hasLocation*/);
+ }
+
+ /**
+ * 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.
+ */
+ @Test
+ @SmallTest
+ public void testGetServiceState_withDefaultSubId_withoutLocation() {
+ setLocationPermissions(false);
+
+ verifyServiceStateForSubId(
+ getUriForSubscriptionId(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID),
+ mTestServiceState, false /*hasLocation*/);
+ }
+
+ @Test
+ @SmallTest
+ public void testGetServiceState_withDefaultSubId_withLocation() {
+ setLocationPermissions(true);
+
+ verifyServiceStateForSubId(
+ getUriForSubscriptionId(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID),
+ mTestServiceState, true /*hasLocation*/);
+ }
+
+ /**
+ * 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
+ */
+ @Test
+ @SmallTest
+ public void testGetServiceStateForSubId_withoutLocation() {
+ setLocationPermissions(false);
+
+ verifyServiceStateForSubId(getUriForSubscriptionId(1), mTestServiceStateForSubId1,
+ false /*hasLocation*/);
+ }
+
+ @Test
+ @SmallTest
+ public void testGetServiceStateForSubId_withLocation() {
+ setLocationPermissions(true);
+
+ verifyServiceStateForSubId(getUriForSubscriptionId(1), mTestServiceStateForSubId1,
+ true /*hasLocation*/);
+ }
+
+ /**
+ * Verify that apps target S+ without READ_PRIVILEGED_PHONE_STATE permission can only access
+ * the public columns of ServiceStateTable.
+ */
+ @Test
+ public void testQueryAllColumns_targetS_noReadPrivilege() {
+ setTargetSdkVersion(Build.VERSION_CODES.S);
+ setCanReadPrivilegedPhoneState(false);
+
+ verifyServiceStateWithPublicColumns(mTestServiceState, null /*projection*/);
+ }
+
+ /**
+ * Verify that apps target S+ without READ_PRIVILEGED_PHONE_STATE permission try to access
+ * non-public columns should throw IllegalArgumentException.
+ */
+ @Test
+ public void testQueryNonPublicColumn_targetS_noReadPrivilege() {
+ setTargetSdkVersion(Build.VERSION_CODES.S);
+ setCanReadPrivilegedPhoneState(false);
+
+ // DATA_ROAMING_TYPE is a non-public column
+ String[] projection = new String[]{DATA_ROAMING_TYPE};
+
+ assertThrows(IllegalArgumentException.class,
+ () -> verifyServiceStateWithPublicColumns(mTestServiceState, projection));
+ }
+
+ /**
+ * Verify that apps target S+ with READ_PRIVILEGED_PHONE_STATE and location permissions should
+ * be able to access all columns.
+ */
+ @Test
+ public void testQueryAllColumn_targetS_withAllPermission() {
+ setTargetSdkVersion(Build.VERSION_CODES.S);
+ setCanReadPrivilegedPhoneState(true);
+ setLocationPermissions(true);
+
+ verifyServiceStateForSubId(
+ getUriForSubscriptionId(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID),
+ mTestServiceState, true /*hasPermission*/);
+ }
+
+ private void verifyServiceStateWithPublicColumns(ServiceState ss, String[] projection) {
+ try (Cursor cursor = mContentResolver.query(ServiceStateTable.CONTENT_URI, projection, null,
+ null)) {
+ assertNotNull(cursor);
+ assertEquals(cursor.getColumnCount(), ServiceStateProvider.PUBLIC_COLUMNS.length);
+
+ cursor.moveToFirst();
+ assertEquals(ss.getVoiceRegState(),
+ cursor.getInt(cursor.getColumnIndex(VOICE_REG_STATE)));
+ assertEquals(ss.getDataRegistrationState(),
+ cursor.getInt(cursor.getColumnIndex(DATA_REG_STATE)));
+ assertEquals(ss.getOperatorNumeric(),
+ cursor.getString(cursor.getColumnIndex(VOICE_OPERATOR_NUMERIC)));
+ assertEquals(ss.getDataNetworkType(),
+ cursor.getInt(cursor.getColumnIndex(DATA_NETWORK_TYPE)));
+ assertEquals(ss.getDuplexMode(), cursor.getInt(cursor.getColumnIndex(DUPLEX_MODE)));
+ }
+ }
+
+ private void verifyServiceStateForSubId(Uri uri, ServiceState ss, boolean hasLocation) {
+ Cursor cursor = mContentResolver.query(uri, ServiceStateProvider.ALL_COLUMNS, "",
null, null);
assertNotNull(cursor);
cursor.moveToFirst();
final int voiceRegState = ss.getState();
final int dataRegState = ss.getDataRegistrationState();
- 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 voiceRoamingType = ss.getVoiceRoamingType();
+ final int dataRoamingType = ss.getDataRoamingType();
+ final String voiceOperatorAlphaLong = hasLocation ? ss.getOperatorAlphaLong() : null;
+ final String voiceOperatorAlphaShort = hasLocation ? ss.getOperatorAlphaShort() : null;
+ final String voiceOperatorNumeric = hasLocation ? ss.getOperatorNumeric() : null;
+ final String dataOperatorAlphaLong = hasLocation ? ss.getOperatorAlphaLong() : null;
+ final String dataOperatorAlphaShort = hasLocation ? ss.getOperatorAlphaShort() : null;
+ final String dataOperatorNumeric = hasLocation ? ss.getOperatorNumeric() : null;
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 networkId = hasLocation ? ss.getCdmaNetworkId() : ServiceState.UNKNOWN_ID;
+ final int systemId = hasLocation ? ss.getCdmaSystemId() : ServiceState.UNKNOWN_ID;
final int cdmaRoamingIndicator = ss.getCdmaRoamingIndicator();
final int cdmaDefaultRoamingIndicator = ss.getCdmaDefaultRoamingIndicator();
final int cdmaEriIconIndex = ss.getCdmaEriIconIndex();
@@ -210,28 +321,30 @@
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));
- assertEquals(dataNetworkType, cursor.getInt(22));
- assertEquals(duplexMode, cursor.getInt(23));
+ assertEquals(voiceRoamingType, cursor.getInt(2));
+ assertEquals(dataRoamingType, cursor.getInt(3));
+ assertEquals(voiceOperatorAlphaLong, cursor.getString(4));
+ assertEquals(voiceOperatorAlphaShort, cursor.getString(5));
+ assertEquals(voiceOperatorNumeric, cursor.getString(6));
+ assertEquals(dataOperatorAlphaLong, cursor.getString(7));
+ assertEquals(dataOperatorAlphaShort, cursor.getString(8));
+ assertEquals(dataOperatorNumeric, cursor.getString(9));
+ assertEquals(isManualNetworkSelection, cursor.getInt(10));
+ assertEquals(rilVoiceRadioTechnology, cursor.getInt(11));
+ assertEquals(rilDataRadioTechnology, cursor.getInt(12));
+ assertEquals(cssIndicator, cursor.getInt(13));
+ assertEquals(networkId, cursor.getInt(14));
+ assertEquals(systemId, cursor.getInt(15));
+ assertEquals(cdmaRoamingIndicator, cursor.getInt(16));
+ assertEquals(cdmaDefaultRoamingIndicator, cursor.getInt(17));
+ assertEquals(cdmaEriIconIndex, cursor.getInt(18));
+ assertEquals(cdmaEriIconMode, cursor.getInt(19));
+ assertEquals(isEmergencyOnly, cursor.getInt(20));
+ assertEquals(isUsingCarrierAggregation, cursor.getInt(21));
+ assertEquals(operatorAlphaLongRaw, cursor.getString(22));
+ assertEquals(operatorAlphaShortRaw, cursor.getString(23));
+ assertEquals(dataNetworkType, cursor.getInt(24));
+ assertEquals(duplexMode, cursor.getInt(25));
}
/**
@@ -370,4 +483,50 @@
}
return false;
}
+
+ private void setLocationPermissions(boolean hasPermission) {
+ if (!hasPermission) {
+ // System location off, LocationAccessPolicy#checkLocationPermission returns DENIED_SOFT
+ when(mLocationManager.isLocationEnabledForUser(any(UserHandle.class)))
+ .thenReturn(false);
+ } else {
+ // Turn on all to let LocationAccessPolicy#checkLocationPermission returns ALLOWED
+ when(mContext.checkPermission(eq(Manifest.permission.ACCESS_FINE_LOCATION),
+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+
+ when(mContext.checkPermission(eq(Manifest.permission.ACCESS_COARSE_LOCATION),
+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+
+ when(mAppOpsManager.noteOpNoThrow(eq(AppOpsManager.OPSTR_FINE_LOCATION),
+ anyInt(), anyString(), nullable(String.class), nullable(String.class)))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+ when(mAppOpsManager.noteOpNoThrow(eq(AppOpsManager.OPSTR_COARSE_LOCATION),
+ anyInt(), anyString(), nullable(String.class), nullable(String.class)))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+
+ when(mLocationManager.isLocationEnabledForUser(any(UserHandle.class))).thenReturn(true);
+ when(mContext.checkPermission(eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL),
+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+ }
+ }
+
+ private <T> void mockSystemService(Class<T> clazz , T obj, String serviceName) {
+ when(mContext.getSystemServiceName(eq(clazz))).thenReturn(serviceName);
+ when(mContext.getSystemService(eq(serviceName))).thenReturn(obj);
+ }
+
+ private void setTargetSdkVersion(int version) {
+ ApplicationInfo testAppInfo = new ApplicationInfo();
+ testAppInfo.targetSdkVersion = version;
+ try {
+ when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
+ .thenReturn(testAppInfo);
+ } catch (Exception ignored) {
+ }
+ }
+
+ private void setCanReadPrivilegedPhoneState(boolean granted) {
+ doReturn(granted ? PERMISSION_GRANTED : PERMISSION_DENIED).when(mContext)
+ .checkCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ }
}