| /* |
| * Copyright (C) 2009 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 android.telephony.cts; |
| |
| import static android.app.AppOpsManager.OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER; |
| import static android.telephony.DataSpecificRegistrationInfo.LTE_ATTACH_EXTRA_INFO_CSFB_NOT_PREFERRED; |
| import static android.telephony.DataSpecificRegistrationInfo.LTE_ATTACH_EXTRA_INFO_NONE; |
| import static android.telephony.DataSpecificRegistrationInfo.LTE_ATTACH_EXTRA_INFO_SMS_ONLY; |
| import static android.telephony.DataSpecificRegistrationInfo.LTE_ATTACH_TYPE_COMBINED; |
| import static android.telephony.DataSpecificRegistrationInfo.LTE_ATTACH_TYPE_EPS_ONLY; |
| import static android.telephony.DataSpecificRegistrationInfo.LTE_ATTACH_TYPE_UNKNOWN; |
| import static android.telephony.PhoneCapability.DEVICE_NR_CAPABILITY_NSA; |
| import static android.telephony.PhoneCapability.DEVICE_NR_CAPABILITY_SA; |
| |
| import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import static org.junit.Assert.assertArrayEquals; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertThrows; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| import static org.junit.Assume.assumeNoException; |
| import static org.junit.Assume.assumeTrue; |
| |
| import android.Manifest; |
| import android.Manifest.permission; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.app.AppOpsManager; |
| import android.app.UiAutomation; |
| import android.bluetooth.BluetoothAdapter; |
| import android.content.BroadcastReceiver; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.PackageInfo; |
| import android.content.pm.PackageManager; |
| import android.content.res.Resources; |
| import android.net.ConnectivityManager; |
| import android.net.Uri; |
| import android.net.wifi.WifiManager; |
| import android.os.AsyncTask; |
| import android.os.Build; |
| import android.os.Bundle; |
| import android.os.Looper; |
| import android.os.Parcel; |
| import android.os.PersistableBundle; |
| import android.os.Process; |
| import android.os.SystemClock; |
| import android.os.SystemProperties; |
| import android.os.UserManager; |
| import android.platform.test.annotations.RequiresFlagsEnabled; |
| import android.platform.test.flag.junit.CheckFlagsRule; |
| import android.platform.test.flag.junit.DeviceFlagsValueProvider; |
| import android.telecom.PhoneAccount; |
| import android.telecom.PhoneAccountHandle; |
| import android.telecom.TelecomManager; |
| import android.telephony.AccessNetworkConstants; |
| import android.telephony.Annotation.RadioPowerState; |
| import android.telephony.AvailableNetworkInfo; |
| import android.telephony.CallAttributes; |
| import android.telephony.CallForwardingInfo; |
| import android.telephony.CallQuality; |
| import android.telephony.CarrierConfigManager; |
| import android.telephony.CellBroadcastIdRange; |
| import android.telephony.CellIdentity; |
| import android.telephony.CellIdentityCdma; |
| import android.telephony.CellIdentityGsm; |
| import android.telephony.CellIdentityLte; |
| import android.telephony.CellIdentityNr; |
| import android.telephony.CellIdentityTdscdma; |
| import android.telephony.CellIdentityWcdma; |
| import android.telephony.CellInfo; |
| import android.telephony.CellLocation; |
| import android.telephony.DataSpecificRegistrationInfo; |
| import android.telephony.DataThrottlingRequest; |
| import android.telephony.ImsiEncryptionInfo; |
| import android.telephony.ModemActivityInfo; |
| import android.telephony.NetworkRegistrationInfo; |
| import android.telephony.PhoneCapability; |
| import android.telephony.PhoneStateListener; |
| import android.telephony.PinResult; |
| import android.telephony.PreciseCallState; |
| import android.telephony.RadioAccessFamily; |
| import android.telephony.RadioAccessSpecifier; |
| import android.telephony.ServiceState; |
| import android.telephony.SignalStrength; |
| import android.telephony.SignalStrengthUpdateRequest; |
| import android.telephony.SignalThresholdInfo; |
| import android.telephony.SmsCbMessage; |
| import android.telephony.SubscriptionInfo; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.TelephonyCallback; |
| import android.telephony.TelephonyManager; |
| import android.telephony.ThermalMitigationRequest; |
| import android.telephony.UiccCardInfo; |
| import android.telephony.UiccPortInfo; |
| import android.telephony.UiccSlotInfo; |
| import android.telephony.UiccSlotMapping; |
| import android.telephony.cts.util.TelephonyUtils; |
| import android.telephony.data.ApnSetting; |
| import android.telephony.data.NetworkSlicingConfig; |
| import android.telephony.emergency.EmergencyNumber; |
| import android.text.TextUtils; |
| import android.util.ArrayMap; |
| import android.util.ArraySet; |
| import android.util.Log; |
| import android.util.Pair; |
| |
| import androidx.test.InstrumentationRegistry; |
| |
| import com.android.compatibility.common.util.AmUtils; |
| import com.android.compatibility.common.util.ApiTest; |
| import com.android.compatibility.common.util.CarrierPrivilegeUtils; |
| import com.android.compatibility.common.util.CddTest; |
| import com.android.compatibility.common.util.PollingCheck; |
| import com.android.compatibility.common.util.ShellIdentityUtils; |
| import com.android.compatibility.common.util.TestThread; |
| import com.android.internal.telephony.flags.Flags; |
| import com.android.internal.telephony.uicc.IccUtils; |
| |
| import org.json.JSONArray; |
| import org.json.JSONException; |
| import org.json.JSONObject; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Ignore; |
| import org.junit.Rule; |
| import org.junit.Test; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.InputStream; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.PublicKey; |
| import java.security.cert.CertificateException; |
| import java.security.cert.CertificateFactory; |
| import java.security.cert.X509Certificate; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.concurrent.CompletableFuture; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.Executor; |
| import java.util.concurrent.LinkedBlockingQueue; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.atomic.AtomicReference; |
| import java.util.function.Consumer; |
| import java.util.function.IntSupplier; |
| import java.util.regex.Pattern; |
| import java.util.stream.Collectors; |
| import java.util.stream.IntStream; |
| |
| /** |
| * Build, install and run the tests by running the commands below: |
| * make cts -j64 |
| * cts-tradefed run cts -m CtsTelephonyTestCases --test android.telephony.cts.TelephonyManagerTest |
| */ |
| public class TelephonyManagerTest { |
| |
| @Rule |
| public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); |
| private TelephonyManager mTelephonyManager; |
| private SubscriptionManager mSubscriptionManager; |
| private PackageManager mPackageManager; |
| private boolean mOnCellLocationChangedCalled = false; |
| private boolean mOnCellInfoChanged = false; |
| private boolean mOnSignalStrengthsChanged = false; |
| private boolean mServiceStateChangedCalled = false; |
| private boolean mRadioRebootTriggered = false; |
| private boolean mHasRadioPowerOff = false; |
| private ServiceState mServiceState; |
| private PhoneCapability mPhoneCapability; |
| private boolean mOnPhoneCapabilityChanged = false; |
| private final Object mLock = new Object(); |
| |
| private CarrierConfigManager mCarrierConfigManager; |
| private String mSelfPackageName; |
| private String mSelfCertHash; |
| |
| private static final int WAIT_FOR_CONDITION = 3000; |
| private static final int TOLERANCE = 1000; |
| private static final int TIMEOUT_FOR_NETWORK_OPS = TOLERANCE * 180; |
| |
| private static final int TIMEOUT_FOR_CARRIER_STATUS_FILE_CHECK = TOLERANCE * 180; |
| private PhoneStateListener mListener; |
| private static ConnectivityManager mCm; |
| private static final String TAG = "TelephonyManagerTest"; |
| private static final List<Integer> ROAMING_TYPES = Arrays.asList( |
| ServiceState.ROAMING_TYPE_DOMESTIC, |
| ServiceState.ROAMING_TYPE_INTERNATIONAL, |
| ServiceState.ROAMING_TYPE_NOT_ROAMING, |
| ServiceState.ROAMING_TYPE_UNKNOWN); |
| private static final List<Integer> NETWORK_TYPES = Arrays.asList( |
| TelephonyManager.NETWORK_TYPE_UNKNOWN, |
| TelephonyManager.NETWORK_TYPE_GPRS, |
| TelephonyManager.NETWORK_TYPE_EDGE, |
| TelephonyManager.NETWORK_TYPE_UMTS, |
| TelephonyManager.NETWORK_TYPE_CDMA, |
| TelephonyManager.NETWORK_TYPE_EVDO_0, |
| TelephonyManager.NETWORK_TYPE_EVDO_A, |
| TelephonyManager.NETWORK_TYPE_1xRTT, |
| TelephonyManager.NETWORK_TYPE_HSDPA, |
| TelephonyManager.NETWORK_TYPE_HSUPA, |
| TelephonyManager.NETWORK_TYPE_HSPA, |
| TelephonyManager.NETWORK_TYPE_IDEN, |
| TelephonyManager.NETWORK_TYPE_EVDO_B, |
| TelephonyManager.NETWORK_TYPE_LTE, |
| TelephonyManager.NETWORK_TYPE_EHRPD, |
| TelephonyManager.NETWORK_TYPE_HSPAP, |
| TelephonyManager.NETWORK_TYPE_GSM, |
| TelephonyManager.NETWORK_TYPE_TD_SCDMA, |
| TelephonyManager.NETWORK_TYPE_IWLAN, |
| TelephonyManager.NETWORK_TYPE_LTE_CA, |
| TelephonyManager.NETWORK_TYPE_NR); |
| |
| private static final int EMERGENCY_NUMBER_SOURCE_RIL_ECCLIST = 0; |
| private static final Set<Integer> EMERGENCY_NUMBER_SOURCE_SET; |
| |
| private static final String PLMN_A = "123456"; |
| private static final String PLMN_B = "78901"; |
| private static final List<String> FPLMN_TEST = Arrays.asList(PLMN_A, PLMN_B); |
| private static final int MAX_FPLMN_NUM = 1000; |
| private static final int MIN_FPLMN_NUM = 3; |
| |
| private static final String THERMAL_MITIGATION_COMMAND_BASE = "cmd phone thermal-mitigation "; |
| private static final String ALLOW_PACKAGE_SUBCOMMAND = "allow-package "; |
| private static final String DISALLOW_PACKAGE_SUBCOMMAND = "disallow-package "; |
| private static final String TELEPHONY_CTS_PACKAGE = "android.telephony.cts"; |
| |
| private static final String TEST_FORWARD_NUMBER = "54321"; |
| private static final String TESTING_PLMN = "12345"; |
| |
| private static final String BAD_IMSI_CERT_URL = "https:badurl.badurl:8080"; |
| private static final String IMSI_CERT_STRING_EPDG = "-----BEGIN CERTIFICATE-----" |
| + "\nMIIDkzCCAnugAwIBAgIEJ4MVZDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJV" |
| + "\nUzEOMAwGA1UECBMFVGV4YXMxDzANBgNVBAcTBklydmluZzEiMCAGA1UEChMZVmVy" |
| + "\naXpvbiBEYXRhIFNlcnZpY2VzIExMQzEMMAoGA1UECxMDTk5PMRgwFgYDVQQDEw9F" |
| + "\nQVAtSURFLlZaVy5DT00wHhcNMTcxMTEzMTkxMTA1WhcNMjcxMTExMTkxMTA1WjB6" |
| + "\nMQswCQYDVQQGEwJVUzEOMAwGA1UECBMFVGV4YXMxDzANBgNVBAcTBklydmluZzEi" |
| + "\nMCAGA1UEChMZVmVyaXpvbiBEYXRhIFNlcnZpY2VzIExMQzEMMAoGA1UECxMDTk5P" |
| + "\nMRgwFgYDVQQDEw9FQVAtSURFLlZaVy5DT00wggEiMA0GCSqGSIb3DQEBAQUAA4IB" |
| + "\nDwAwggEKAoIBAQCrQ28TvN0uUV/vK4YUS7+zcYMKAe5IYtDa3Wa0r64iyBSz6Eau" |
| + "\nT+YHNNzCV4xMqURM5mIY6796LnmWR5jViUgrHyw0d06mLE54uUET/drn2pwhaobK" |
| + "\nNVvbYzpm5W3dvext+klEgIhpRW4fR/uNUmD0O9n/5ofpg++wbvMNWEIjeTVUGPRT" |
| + "\nCeVblH3tK8bKdCKjp48HtuciY7gE8LMoHhMHA1cob9VktSYTy2ABa+rKAPAaqVz4" |
| + "\nL0Arlbi9INHSDNFlLvy1xE5dyYIqhRMicM2i4LCMwJnwf0tz8m7DmDxfdmC4HY2Q" |
| + "\nz4VpbQOu10oRhXXrhZFkZEmqp6RYQmDRDDDtAgMBAAGjITAfMB0GA1UdDgQWBBSg" |
| + "\nFA6liox07smzfITrvjSlgWkMMTANBgkqhkiG9w0BAQsFAAOCAQEAIoFKLgLfS9f1" |
| + "\n0UG85rb+noaeXY0YofSY0dxFIW3rA5zjRD0kus9iyw9CfADDD305hefJ4Kq/NLAF" |
| + "\n0odR4MOTan5KhXTlD9/8mZjSSeEktgCX3BbmMqKoKcaV6Oo9C0RfwGccDms6D+Dw" |
| + "\n3GkgsvKJEB8LjApzQSmDwCV9BVJsC60041cndqBxMr3RMxCkO6/sQRKyAuzx5f91" |
| + "\nWn5cpYxvl4//TatSc9oeU+ootlxfXszdRPM5xqCodm6gWmxRkK6DePlhpaZ1sKdw" |
| + "\nCQg/mA35Eh5ZgOpZT2YG+a8BbDRCF5gj/pu1tPt8VfApPHq6lAoitlrx1cEdJWx6" |
| + "\n5JXaFrs0UA==" |
| + "\n-----END CERTIFICATE-----"; |
| private static final String IMSI_CERT_STRING_WLAN = "-----BEGIN CERTIFICATE-----" |
| + "\nMIIFbzCCBFegAwIBAgIUAz8I/cK3fILeJ9PSbi7MkN8yZBkwDQYJKoZIhvcNAQEL" |
| + "\nBQAwgY0xCzAJBgNVBAYTAk5MMRIwEAYDVQQHEwlBbXN0ZXJkYW0xJTAjBgNVBAoT" |
| + "\nHFZlcml6b24gRW50ZXJwcmlzZSBTb2x1dGlvbnMxEzARBgNVBAsTCkN5YmVydHJ1" |
| + "\nc3QxLjAsBgNVBAMTJVZlcml6b24gUHVibGljIFN1cmVTZXJ2ZXIgQ0EgRzE0LVNI" |
| + "\nQTIwHhcNMTcxMTE2MTU1NjMzWhcNMTkxMTE2MTU1NjMzWjB6MQswCQYDVQQGEwJV" |
| + "\nUzEOMAwGA1UECBMFVGV4YXMxDzANBgNVBAcTBklydmluZzEiMCAGA1UEChMZVmVy" |
| + "\naXpvbiBEYXRhIFNlcnZpY2VzIExMQzEMMAoGA1UECxMDTk5PMRgwFgYDVQQDEw9F" |
| + "\nQVAtSURFLlZaVy5DT00wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCr" |
| + "\nQ28TvN0uUV/vK4YUS7+zcYMKAe5IYtDa3Wa0r64iyBSz6EauT+YHNNzCV4xMqURM" |
| + "\n5mIY6796LnmWR5jViUgrHyw0d06mLE54uUET/drn2pwhaobKNVvbYzpm5W3dvext" |
| + "\n+klEgIhpRW4fR/uNUmD0O9n/5ofpg++wbvMNWEIjeTVUGPRTCeVblH3tK8bKdCKj" |
| + "\np48HtuciY7gE8LMoHhMHA1cob9VktSYTy2ABa+rKAPAaqVz4L0Arlbi9INHSDNFl" |
| + "\nLvy1xE5dyYIqhRMicM2i4LCMwJnwf0tz8m7DmDxfdmC4HY2Qz4VpbQOu10oRhXXr" |
| + "\nhZFkZEmqp6RYQmDRDDDtAgMBAAGjggHXMIIB0zAMBgNVHRMBAf8EAjAAMEwGA1Ud" |
| + "\nIARFMEMwQQYJKwYBBAGxPgEyMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vc2VjdXJl" |
| + "\nLm9tbmlyb290LmNvbS9yZXBvc2l0b3J5MIGpBggrBgEFBQcBAQSBnDCBmTAtBggr" |
| + "\nBgEFBQcwAYYhaHR0cDovL3Zwc3NnMTQyLm9jc3Aub21uaXJvb3QuY29tMDMGCCsG" |
| + "\nAQUFBzAChidodHRwOi8vY2FjZXJ0Lm9tbmlyb290LmNvbS92cHNzZzE0Mi5jcnQw" |
| + "\nMwYIKwYBBQUHMAKGJ2h0dHA6Ly9jYWNlcnQub21uaXJvb3QuY29tL3Zwc3NnMTQy" |
| + "\nLmRlcjAaBgNVHREEEzARgg9FQVAtSURFLlZaVy5DT00wDgYDVR0PAQH/BAQDAgWg" |
| + "\nMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAfBgNVHSMEGDAWgBTkLbuR" |
| + "\nAWUmH7R6P6MVJaTOjEQzOzA+BgNVHR8ENzA1MDOgMaAvhi1odHRwOi8vdnBzc2cx" |
| + "\nNDIuY3JsLm9tbmlyb290LmNvbS92cHNzZzE0Mi5jcmwwHQYDVR0OBBYEFKAUDqWK" |
| + "\njHTuybN8hOu+NKWBaQwxMA0GCSqGSIb3DQEBCwUAA4IBAQAbSrvVrdxRPLnVu6vc" |
| + "\n4BiFT2gWDhZ63EyV4f877sC1iMJRFlfwWQQfHVyhGTFa8JnhbEhhTxCP+L00Q8rX" |
| + "\nKbOw9ei5g2yp7OjStwhHz5T20UejjKkl7hKtMduZXxFToqhVwIpqG58Tzl/35FX4" |
| + "\nu+YDPgwTX5gbpbJxpbncn9voxWGWu3AbHVvzaskfBgZfWAuJnbgq0WTEt7bGOfiI" |
| + "\nelIIQe7XL6beFcdAM9C7DlgOLqpR/31LncrMC46cPA5HmfV4mnpeK/9uq0mMbUJK" |
| + "\nx2vNRWONSm2UGwdb00tLsTloxeqCOMpbkBiqi/RhOlIKIOWMPojukA5+xryh2FVs" |
| + "\n7bdw" |
| + "\n-----END CERTIFICATE-----"; |
| |
| private static final int RADIO_HAL_VERSION_1_5 = makeRadioVersion(1, 5); |
| private static final int RADIO_HAL_VERSION_1_6 = makeRadioVersion(1, 6); |
| private static final int RADIO_HAL_VERSION_2_0 = makeRadioVersion(2, 0); |
| private static final int RADIO_HAL_VERSION_2_1 = makeRadioVersion(2, 1); |
| private static final int RADIO_HAL_VERSION_2_2 = makeRadioVersion(2, 2); |
| |
| static { |
| EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>(); |
| EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING); |
| EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM); |
| EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE); |
| EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG); |
| EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT); |
| } |
| |
| private static final Set<Integer> EMERGENCY_SERVICE_CATEGORY_SET; |
| static { |
| EMERGENCY_SERVICE_CATEGORY_SET = new HashSet<Integer>(); |
| EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE); |
| EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE); |
| EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE); |
| EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD); |
| EMERGENCY_SERVICE_CATEGORY_SET.add( |
| EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE); |
| EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC); |
| EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC); |
| } |
| |
| private static final Map<Class<? extends CellIdentity>, List<Integer>> sNetworkTypes; |
| static { |
| sNetworkTypes = new ArrayMap<>(); |
| sNetworkTypes.put(CellIdentityGsm.class, |
| Arrays.asList(new Integer[]{ |
| TelephonyManager.NETWORK_TYPE_GSM, |
| TelephonyManager.NETWORK_TYPE_GPRS, |
| TelephonyManager.NETWORK_TYPE_EDGE})); |
| sNetworkTypes.put(CellIdentityWcdma.class, |
| Arrays.asList(TelephonyManager.NETWORK_TYPE_UMTS, |
| TelephonyManager.NETWORK_TYPE_HSDPA, |
| TelephonyManager.NETWORK_TYPE_HSUPA, |
| TelephonyManager.NETWORK_TYPE_HSPA, |
| TelephonyManager.NETWORK_TYPE_HSPAP)); |
| sNetworkTypes.put(CellIdentityCdma.class, |
| Arrays.asList(TelephonyManager.NETWORK_TYPE_CDMA, |
| TelephonyManager.NETWORK_TYPE_1xRTT, |
| TelephonyManager.NETWORK_TYPE_EVDO_0, |
| TelephonyManager.NETWORK_TYPE_EVDO_A, |
| TelephonyManager.NETWORK_TYPE_EVDO_B, |
| TelephonyManager.NETWORK_TYPE_EHRPD)); |
| sNetworkTypes.put(CellIdentityLte.class, |
| Arrays.asList(TelephonyManager.NETWORK_TYPE_LTE)); |
| sNetworkTypes.put(CellIdentityNr.class, |
| Arrays.asList(TelephonyManager.NETWORK_TYPE_NR)); |
| sNetworkTypes.put(CellIdentityTdscdma.class, |
| Arrays.asList(TelephonyManager.NETWORK_TYPE_TD_SCDMA)); |
| } |
| |
| private int mTestSub; |
| private int mNetworkHalVersion; |
| private int mModemHalVersion; |
| private int mConfigHalVersion; |
| private boolean mIsAllowedNetworkTypeChanged; |
| private Map<Integer, Long> mAllowedNetworkTypesList = new HashMap<>(); |
| |
| private static final String CARRIER_RESTRICTION_OPERATOR_DETAILS = "{\"com.vzw.hss" |
| + ".myverizon\":{\"carrierId\":1839," |
| + "\"callerSHA1Id\":[\"C58EE7871896786F8BF70EBDB137DE10074043E9\"," |
| + "\"AE23A03436DF07B0CD70FE881CDA2EC1D21215D7B7B0CC68E67B67F5DF89526A\"]}}"; |
| |
| private class CarrierPrivilegeChangeMonitor implements AutoCloseable { |
| // CarrierPrivilegesCallback will be triggered upon registration. Filter the first callback |
| // here since we really care of the *change* of carrier privileges instead of the content |
| private boolean mHasSentPrivilegeChangeCallback = false; |
| private CountDownLatch mLatch = new CountDownLatch(1); |
| private final TelephonyManager.CarrierPrivilegesCallback mCarrierPrivilegesCallback; |
| |
| CarrierPrivilegeChangeMonitor() { |
| mCarrierPrivilegesCallback = (privilegedPackageNames, privilegedUids) -> { |
| // Ignore the first callback which is triggered upon registration |
| if (!mHasSentPrivilegeChangeCallback) { |
| mHasSentPrivilegeChangeCallback = true; |
| return; |
| } |
| mLatch.countDown(); |
| }; |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.registerCarrierPrivilegesCallback( |
| SubscriptionManager.getSlotIndex(mTestSub), |
| getContext().getMainExecutor(), |
| mCarrierPrivilegesCallback)); |
| } |
| |
| public void waitForCarrierPrivilegeChanged() throws Exception { |
| if (!mLatch.await(5, TimeUnit.SECONDS)) { |
| throw new IllegalStateException("Failed to update carrier privileges"); |
| } |
| } |
| |
| @Override |
| public void close() throws Exception { |
| if(mTelephonyManager != null) { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.unregisterCarrierPrivilegesCallback( |
| mCarrierPrivilegesCallback)); |
| } |
| } |
| } |
| |
| private static class CountryChangedReceiver extends BroadcastReceiver { |
| private CountDownLatch mLatch = new CountDownLatch(1); |
| |
| @Nullable |
| private Bundle mBundle; |
| |
| @Nullable |
| public Bundle getExtras() { |
| return mBundle; |
| } |
| |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| if (TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED.equals(intent.getAction())) { |
| Log.d(TAG, "testLastKnownCountryIso received ACTION_NETWORK_COUNTRY_CHANGED"); |
| mBundle = intent.getExtras(); |
| mLatch.countDown(); |
| } |
| } |
| |
| void clearQueue() { |
| mLatch = new CountDownLatch(1); |
| } |
| |
| void waitForIntent() throws Exception { |
| // Extend to wait up to 10 seconds to receive CountryChanged Intent. |
| mLatch.await(10000, TimeUnit.MILLISECONDS); |
| } |
| } |
| |
| @Before |
| public void setUp() throws Exception { |
| mCm = getContext().getSystemService(ConnectivityManager.class); |
| mPackageManager = getContext().getPackageManager(); |
| assumeTrue(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)); |
| |
| mSubscriptionManager = getContext().getSystemService(SubscriptionManager.class); |
| mCarrierConfigManager = getContext().getSystemService(CarrierConfigManager.class); |
| mSelfPackageName = getContext().getPackageName(); |
| mSelfCertHash = getCertHash(mSelfPackageName); |
| mTestSub = SubscriptionManager.getDefaultSubscriptionId(); |
| // If the test subscription is invalid, TelephonyManager APIs may return null |
| assumeTrue("Skipping tests because default subscription ID is invalid", |
| mTestSub != SubscriptionManager.INVALID_SUBSCRIPTION_ID); |
| mTelephonyManager = getContext().getSystemService(TelephonyManager.class) |
| .createForSubscriptionId(mTestSub); |
| try { |
| mTelephonyManager.getHalVersion(TelephonyManager.HAL_SERVICE_RADIO); |
| } catch (IllegalStateException e) { |
| assumeNoException("Skipping tests because Telephony service is null", e); |
| } |
| Pair<Integer, Integer> networkHalVersion = |
| mTelephonyManager.getHalVersion(TelephonyManager.HAL_SERVICE_NETWORK); |
| mNetworkHalVersion = makeRadioVersion(networkHalVersion.first, networkHalVersion.second); |
| Pair<Integer, Integer> modemHalVersion = |
| mTelephonyManager.getHalVersion(TelephonyManager.HAL_SERVICE_MODEM); |
| mModemHalVersion = makeRadioVersion(modemHalVersion.first, modemHalVersion.second); |
| Pair<Integer, Integer> simHalVersion = |
| mTelephonyManager.getHalVersion(TelephonyManager.HAL_SERVICE_RADIO); |
| mConfigHalVersion = makeRadioVersion(simHalVersion.first, simHalVersion.second); |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .adoptShellPermissionIdentity(android.Manifest.permission.READ_PHONE_STATE); |
| saveAllowedNetworkTypesForAllReasons(); |
| |
| // Wait previously queued broadcasts to complete before starting the test |
| AmUtils.waitForBroadcastBarrier(); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| if (mListener != null) { |
| // unregister the listener |
| mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE); |
| } |
| if (mIsAllowedNetworkTypeChanged) { |
| recoverAllowedNetworkType(); |
| } |
| |
| StringBuilder cmdBuilder = new StringBuilder(); |
| cmdBuilder.append(THERMAL_MITIGATION_COMMAND_BASE).append(DISALLOW_PACKAGE_SUBCOMMAND) |
| .append(TELEPHONY_CTS_PACKAGE); |
| TelephonyUtils.executeShellCommand(InstrumentationRegistry.getInstrumentation(), |
| cmdBuilder.toString()); |
| } |
| |
| private void saveAllowedNetworkTypesForAllReasons() { |
| mIsAllowedNetworkTypeChanged = false; |
| if (mAllowedNetworkTypesList == null) { |
| mAllowedNetworkTypesList = new HashMap<>(); |
| } |
| long allowedNetworkTypesUser = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.getAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER) |
| ); |
| long allowedNetworkTypesPower = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.getAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER) |
| ); |
| long allowedNetworkTypesCarrier = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.getAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER) |
| ); |
| long allowedNetworkTypesEnable2g = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.getAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G) |
| ); |
| mAllowedNetworkTypesList.put(TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER, |
| allowedNetworkTypesUser); |
| mAllowedNetworkTypesList.put(TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER, |
| allowedNetworkTypesPower); |
| mAllowedNetworkTypesList.put(TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER, |
| allowedNetworkTypesCarrier); |
| mAllowedNetworkTypesList.put(TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, |
| allowedNetworkTypesEnable2g); |
| } |
| |
| private void recoverAllowedNetworkType() { |
| if (mAllowedNetworkTypesList == null) { |
| return; |
| } |
| for (Integer key : mAllowedNetworkTypesList.keySet()) { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setAllowedNetworkTypesForReason( |
| key, |
| mAllowedNetworkTypesList.get(key))); |
| } |
| } |
| |
| private String getCertHash(String pkgName) throws Exception { |
| try { |
| PackageInfo pInfo = mPackageManager.getPackageInfo(pkgName, |
| PackageManager.GET_SIGNATURES |
| | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS); |
| MessageDigest md = MessageDigest.getInstance("SHA-1"); |
| return IccUtils.bytesToHexString(md.digest(pInfo.signatures[0].toByteArray())); |
| } catch (PackageManager.NameNotFoundException ex) { |
| Log.e(TAG, pkgName + " not found", ex); |
| throw ex; |
| } catch (NoSuchAlgorithmException ex) { |
| Log.e(TAG, "Algorithm SHA1 is not found."); |
| throw ex; |
| } |
| } |
| |
| /** Checks whether the telephony feature is supported. */ |
| private boolean hasFeature(String feature) { |
| return mPackageManager.hasSystemFeature(feature); |
| } |
| |
| @Test |
| public void testHasCarrierPrivilegesViaCarrierConfigs() throws Exception { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mTestSub); |
| |
| try { |
| assertNotNull("CarrierConfigManager#getConfigForSubId() returned null", |
| carrierConfig); |
| assertFalse("CarrierConfigManager#getConfigForSubId() returned empty bundle", |
| carrierConfig.isEmpty()); |
| |
| // purge the certs in carrierConfigs first |
| carrierConfig.putStringArray( |
| CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY, new String[]{}); |
| changeCarrierPrivileges(false, carrierConfig); |
| // verify we don't have privilege through carrierConfigs or Uicc |
| assertFalse(mTelephonyManager.hasCarrierPrivileges()); |
| |
| carrierConfig.putStringArray( |
| CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY, |
| new String[]{mSelfCertHash}); |
| |
| // verify we now have privilege after adding certificate to carrierConfigs |
| changeCarrierPrivileges(true, carrierConfig); |
| assertTrue(mTelephonyManager.hasCarrierPrivileges()); |
| } finally { |
| // purge the newly added certificate |
| carrierConfig.putStringArray( |
| CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY, new String[]{}); |
| changeCarrierPrivileges(false, carrierConfig); |
| // verify we no longer have privilege after removing certificate |
| assertFalse(mTelephonyManager.hasCarrierPrivileges()); |
| } |
| } |
| |
| private void changeCarrierPrivileges(boolean gain, PersistableBundle carrierConfig) |
| throws Exception { |
| if (mTelephonyManager.hasCarrierPrivileges() == gain) { |
| Log.w(TAG, "Carrier privileges already " + (gain ? "granted" : "revoked")); |
| return; |
| } |
| |
| try(CarrierPrivilegeChangeMonitor monitor = new CarrierPrivilegeChangeMonitor()) { |
| overrideCarrierConfig(carrierConfig); |
| monitor.waitForCarrierPrivilegeChanged(); |
| } |
| } |
| |
| private void overrideCarrierConfig(PersistableBundle bundle) throws Exception { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCarrierConfigManager, |
| (cm) -> cm.overrideConfig(mTestSub, bundle)); |
| } |
| |
| public static void grantLocationPermissions() { |
| UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); |
| String packageName = getContext().getPackageName(); |
| uiAutomation.grantRuntimePermission(packageName, permission.ACCESS_COARSE_LOCATION); |
| uiAutomation.grantRuntimePermission(packageName, permission.ACCESS_FINE_LOCATION); |
| uiAutomation.grantRuntimePermission(packageName, permission.ACCESS_BACKGROUND_LOCATION); |
| } |
| |
| @Test |
| public void testDevicePolicyApn() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_DATA)); |
| |
| // These methods aren't accessible to anything except system and phone by design, so we just |
| // look for security exceptions here. |
| try { |
| List<ApnSetting> apns = mTelephonyManager.getDevicePolicyOverrideApns(getContext()); |
| fail("SecurityException expected"); |
| } catch (SecurityException e) { |
| // expected |
| } |
| |
| try { |
| ApnSetting.Builder builder = new ApnSetting.Builder(); |
| |
| ApnSetting setting = builder |
| .setEntryName("asdf") |
| .setApnName("asdf") |
| .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT) |
| .build(); |
| int id = mTelephonyManager.addDevicePolicyOverrideApn(getContext(), setting); |
| fail("SecurityException expected"); |
| } catch (SecurityException e) { |
| // expected |
| } |
| |
| try { |
| ApnSetting.Builder builder = new ApnSetting.Builder(); |
| |
| ApnSetting setting = builder |
| .setEntryName("asdf") |
| .setApnName("asdf") |
| .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT) |
| .build(); |
| boolean success = mTelephonyManager.modifyDevicePolicyOverrideApn( |
| getContext(), 0, setting); |
| fail("SecurityException expected"); |
| } catch (SecurityException e) { |
| // expected |
| } |
| } |
| |
| @Test |
| public void testListen() throws Throwable { |
| if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { |
| // TODO: temp workaround, need to adjust test to for CDMA |
| return; |
| } |
| |
| grantLocationPermissions(); |
| |
| TestThread t = new TestThread(() -> { |
| Looper.prepare(); |
| mListener = new PhoneStateListener() { |
| @Override |
| public void onCellLocationChanged(CellLocation location) { |
| if (!mOnCellLocationChangedCalled) { |
| synchronized (mLock) { |
| mOnCellLocationChangedCalled = true; |
| mLock.notify(); |
| } |
| } |
| } |
| }; |
| |
| synchronized (mLock) { |
| mLock.notify(); // mListener is ready |
| } |
| |
| Looper.loop(); |
| }); |
| |
| synchronized (mLock) { |
| t.start(); |
| mLock.wait(TOLERANCE); // wait for mListener |
| } |
| |
| // Test register |
| synchronized (mLock) { |
| // .listen generates an onCellLocationChanged event |
| mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_CELL_LOCATION); |
| mLock.wait(TOLERANCE); |
| |
| assertTrue("Test register, mOnCellLocationChangedCalled should be true.", |
| mOnCellLocationChangedCalled); |
| } |
| |
| synchronized (mLock) { |
| mOnCellLocationChangedCalled = false; |
| CellLocation.requestLocationUpdate(); |
| mLock.wait(TOLERANCE); |
| |
| // Starting with Android S, this API will silently drop all requests from apps |
| // targeting Android S due to unfixable limitations with the API. |
| assertFalse("Test register, mOnCellLocationChangedCalled should be false.", |
| mOnCellLocationChangedCalled); |
| } |
| |
| // unregister the listener |
| mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE); |
| Thread.sleep(TOLERANCE); |
| |
| // Test unregister |
| synchronized (mLock) { |
| mOnCellLocationChangedCalled = false; |
| // unregister again, to make sure doing so does not call the listener |
| mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE); |
| CellLocation.requestLocationUpdate(); |
| mLock.wait(TOLERANCE); |
| |
| assertFalse("Test unregister, mOnCellLocationChangedCalled should be false.", |
| mOnCellLocationChangedCalled); |
| } |
| } |
| |
| /** |
| * The getter methods here are all related to the information about the telephony. |
| * These getters are related to concrete location, phone, service provider company, so |
| * it's no need to get details of these information, just make sure they are in right |
| * condition(>0 or not null). |
| */ |
| @Test |
| public void testTelephonyManager() { |
| assertTrue(mTelephonyManager.getNetworkType() >= TelephonyManager.NETWORK_TYPE_UNKNOWN); |
| assertTrue(mTelephonyManager.getPhoneType() >= TelephonyManager.PHONE_TYPE_NONE); |
| assertTrue(mTelephonyManager.getSimState() >= TelephonyManager.SIM_STATE_UNKNOWN); |
| assertTrue(mTelephonyManager.getDataActivity() >= TelephonyManager.DATA_ACTIVITY_NONE); |
| assertTrue(mTelephonyManager.getDataState() >= TelephonyManager.DATA_DISCONNECTED); |
| assertTrue(mTelephonyManager.getCallState() >= TelephonyManager.CALL_STATE_IDLE); |
| |
| for (int i = 0; i < mTelephonyManager.getPhoneCount(); ++i) { |
| assertTrue(mTelephonyManager.getSimState(i) >= TelephonyManager.SIM_STATE_UNKNOWN); |
| } |
| |
| // Make sure devices without MMS service won't fail on this |
| if (InstrumentationRegistry.getContext().getPackageManager() |
| .hasSystemFeature(PackageManager.FEATURE_TELEPHONY) |
| && (mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE)) { |
| assertFalse(mTelephonyManager.getMmsUserAgent().isEmpty()); |
| assertFalse(mTelephonyManager.getMmsUAProfUrl().isEmpty()); |
| } |
| |
| // The following methods may return any value depending on the state of the device. Simply |
| // call them to make sure they do not throw any exceptions. |
| mTelephonyManager.getVoiceMailNumber(); |
| mTelephonyManager.getSimOperatorName(); |
| mTelephonyManager.getNetworkCountryIso(); |
| mTelephonyManager.getCellLocation(); |
| mTelephonyManager.getSimCarrierId(); |
| mTelephonyManager.getSimCarrierIdName(); |
| mTelephonyManager.getSimSpecificCarrierId(); |
| mTelephonyManager.getSimSpecificCarrierIdName(); |
| mTelephonyManager.getCarrierIdFromSimMccMnc(); |
| mTelephonyManager.isDataRoamingEnabled(); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getSimSerialNumber()); |
| mTelephonyManager.getSimOperator(); |
| mTelephonyManager.getSignalStrength(); |
| mTelephonyManager.getNetworkOperatorName(); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getSubscriberId()); |
| mTelephonyManager.getLine1Number(); |
| mTelephonyManager.getNetworkOperator(); |
| |
| try { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .adoptShellPermissionIdentity( |
| android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE); |
| mTelephonyManager.getPhoneAccountHandle(); |
| } catch (SecurityException e) { |
| fail("TelephonyManager#getPhoneAccountHandle requires READ_PRIVILEGED_PHONE_STATE"); |
| } finally { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .dropShellPermissionIdentity(); |
| } |
| mTelephonyManager.getSimCountryIso(); |
| mTelephonyManager.getVoiceMailAlphaTag(); |
| mTelephonyManager.isNetworkRoaming(); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getDeviceId()); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getDeviceId(mTelephonyManager.getSlotIndex())); |
| mTelephonyManager.getDeviceSoftwareVersion(); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getDeviceSoftwareVersion(mTelephonyManager.getSlotIndex())); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getImei()); |
| if (mModemHalVersion >= RADIO_HAL_VERSION_2_1) { |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getPrimaryImei()); |
| } |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getImei(mTelephonyManager.getSlotIndex())); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.isManualNetworkSelectionAllowed()); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getManualNetworkSelectionPlmn()); |
| |
| mTelephonyManager.getPhoneCount(); |
| mTelephonyManager.getDataEnabled(); |
| mTelephonyManager.getNetworkSpecifier(); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, (tm) -> tm.getNai()); |
| TelecomManager telecomManager = getContext().getSystemService(TelecomManager.class); |
| PhoneAccountHandle defaultAccount = telecomManager |
| .getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL); |
| mTelephonyManager.getVoicemailRingtoneUri(defaultAccount); |
| mTelephonyManager.isVoicemailVibrationEnabled(defaultAccount); |
| mTelephonyManager.getSubscriptionId(defaultAccount); |
| mTelephonyManager.getCarrierConfig(); |
| mTelephonyManager.isVoiceCapable(); |
| mTelephonyManager.isSmsCapable(); |
| mTelephonyManager.isLteCdmaEvdoGsmWcdmaEnabled(); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.isDataConnectionAllowed()); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.isAnyRadioPoweredOn()); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.resetIms(tm.getSlotIndex())); |
| |
| // Verify TelephonyManager.getCarrierPrivilegeStatus |
| List<Integer> validCarrierPrivilegeStatus = new ArrayList<>(); |
| validCarrierPrivilegeStatus.add(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS); |
| validCarrierPrivilegeStatus.add(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS); |
| validCarrierPrivilegeStatus.add( |
| TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED); |
| validCarrierPrivilegeStatus.add( |
| TelephonyManager.CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES); |
| int carrierPrivilegeStatusResult = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.getCarrierPrivilegeStatus(Process.myUid())); |
| assertTrue(validCarrierPrivilegeStatus.contains(carrierPrivilegeStatusResult)); |
| |
| // Verify TelephonyManager.getCarrierPrivilegedPackagesForAllActiveSubscriptions |
| List<String> resultForGetCarrierPrivilegedApis = |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getCarrierPrivilegedPackagesForAllActiveSubscriptions()); |
| assertNotNull(resultForGetCarrierPrivilegedApis); |
| for (String result : resultForGetCarrierPrivilegedApis) { |
| assertFalse(TextUtils.isEmpty(result)); |
| } |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| TelephonyManager::getDefaultRespondViaMessageApplication); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| TelephonyManager::getAndUpdateDefaultRespondViaMessageApplication); |
| |
| // Verify getImei/getSubscriberId/getIccAuthentication: |
| // With app ops permision USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, should not throw |
| // SecurityException. |
| try { |
| setAppOpsPermissionAllowed(true, OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER); |
| |
| mTelephonyManager.getImei(); |
| if (mModemHalVersion >= RADIO_HAL_VERSION_2_1) { |
| mTelephonyManager.getPrimaryImei(); |
| } |
| mTelephonyManager.getSubscriberId(); |
| mTelephonyManager.getIccAuthentication( |
| TelephonyManager.APPTYPE_USIM, TelephonyManager.AUTHTYPE_EAP_AKA, ""); |
| } finally { |
| setAppOpsPermissionAllowed(false, OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER); |
| } |
| |
| // Verify getIccAuthentication: |
| // With app ops permission USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, should not throw |
| // SecurityException. |
| try { |
| setAppOpsPermissionAllowed(true, OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER); |
| |
| mTelephonyManager.getIccAuthentication( |
| TelephonyManager.APPTYPE_USIM, TelephonyManager.AUTHTYPE_GBA_BOOTSTRAP, ""); |
| } finally { |
| setAppOpsPermissionAllowed(false, OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER); |
| } |
| |
| // Verify getIccAuthentication: |
| // With app ops permission USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, should not throw |
| // SecurityException. |
| try { |
| setAppOpsPermissionAllowed(true, OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER); |
| |
| mTelephonyManager.getIccAuthentication( |
| TelephonyManager.APPTYPE_USIM, TelephonyManager.AUTHTYPE_GBA_NAF_KEY_EXTERNAL, |
| ""); |
| } finally { |
| setAppOpsPermissionAllowed(false, OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER); |
| } |
| } |
| |
| @Test |
| public void testGetCallForwarding() throws Exception { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_CALLING)); |
| |
| List<Integer> callForwardingReasons = new ArrayList<>(); |
| callForwardingReasons.add(CallForwardingInfo.REASON_UNCONDITIONAL); |
| callForwardingReasons.add(CallForwardingInfo.REASON_BUSY); |
| callForwardingReasons.add(CallForwardingInfo.REASON_NO_REPLY); |
| callForwardingReasons.add(CallForwardingInfo.REASON_NOT_REACHABLE); |
| callForwardingReasons.add(CallForwardingInfo.REASON_ALL); |
| callForwardingReasons.add(CallForwardingInfo.REASON_ALL_CONDITIONAL); |
| |
| Set<Integer> callForwardingErrors = new HashSet<Integer>(); |
| callForwardingErrors.add(TelephonyManager.CallForwardingInfoCallback |
| .RESULT_ERROR_FDN_CHECK_FAILURE); |
| callForwardingErrors.add(TelephonyManager.CallForwardingInfoCallback.RESULT_ERROR_UNKNOWN); |
| callForwardingErrors.add(TelephonyManager.CallForwardingInfoCallback |
| .RESULT_ERROR_NOT_SUPPORTED); |
| |
| for (int callForwardingReasonToGet : callForwardingReasons) { |
| Log.d(TAG, "[testGetCallForwarding] callForwardingReasonToGet: " |
| + callForwardingReasonToGet); |
| AtomicReference<CallForwardingInfo> receivedForwardingInfo = new AtomicReference<>(); |
| AtomicReference<Integer> receivedErrorCode = new AtomicReference<>(); |
| CountDownLatch latch = new CountDownLatch(1); |
| TelephonyManager.CallForwardingInfoCallback callback = |
| new TelephonyManager.CallForwardingInfoCallback() { |
| @Override |
| public void onCallForwardingInfoAvailable(CallForwardingInfo info) { |
| receivedForwardingInfo.set(info); |
| latch.countDown(); |
| } |
| |
| @Override |
| public void onError(int error) { |
| receivedErrorCode.set(error); |
| latch.countDown(); |
| } |
| }; |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.getCallForwarding(callForwardingReasonToGet, |
| getContext().getMainExecutor(), callback)); |
| |
| assertTrue(latch.await(TIMEOUT_FOR_NETWORK_OPS, TimeUnit.MILLISECONDS)); |
| // Make sure only one of the callbacks gets invoked |
| assertTrue((receivedForwardingInfo.get() != null) ^ (receivedErrorCode.get() != null)); |
| if (receivedForwardingInfo.get() != null) { |
| CallForwardingInfo info = receivedForwardingInfo.get(); |
| assertTrue("Got reason not in expected set:" + info.getReason(), |
| callForwardingReasons.contains(info.getReason())); |
| if (info.isEnabled()) { |
| assertNotNull(info.getNumber()); |
| assertTrue("Got negative timeoutSeconds=" + info.getTimeoutSeconds(), |
| info.getTimeoutSeconds() >= 0); |
| } |
| } |
| |
| if (receivedErrorCode.get() != null) { |
| assertTrue("Got code not in expected set:" + receivedErrorCode.get(), |
| callForwardingErrors.contains(receivedErrorCode.get())); |
| } |
| } |
| } |
| |
| @Test |
| public void testSetCallForwarding() throws Exception { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_CALLING)); |
| |
| List<Integer> callForwardingReasons = new ArrayList<>(); |
| callForwardingReasons.add(CallForwardingInfo.REASON_UNCONDITIONAL); |
| callForwardingReasons.add(CallForwardingInfo.REASON_BUSY); |
| callForwardingReasons.add(CallForwardingInfo.REASON_NO_REPLY); |
| callForwardingReasons.add(CallForwardingInfo.REASON_NOT_REACHABLE); |
| callForwardingReasons.add(CallForwardingInfo.REASON_ALL); |
| callForwardingReasons.add(CallForwardingInfo.REASON_ALL_CONDITIONAL); |
| |
| // Enable Call Forwarding |
| for (int callForwardingReasonToEnable : callForwardingReasons) { |
| CountDownLatch latch = new CountDownLatch(1); |
| // Disregard success or failure; just make sure it reports back. |
| Consumer<Integer> ignoringResultListener = (x) -> latch.countDown(); |
| |
| final CallForwardingInfo callForwardingInfoToEnable = new CallForwardingInfo( |
| true, |
| callForwardingReasonToEnable, |
| TEST_FORWARD_NUMBER, |
| // time seconds |
| 1); |
| Log.d(TAG, "[testSetCallForwarding] Enable Call Forwarding. Reason: " |
| + callForwardingReasonToEnable + " Number: " + TEST_FORWARD_NUMBER |
| + " Time Seconds: 1"); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setCallForwarding(callForwardingInfoToEnable, |
| getContext().getMainExecutor(), ignoringResultListener)); |
| // TODO: this takes way too long on a real network (upwards of 40s). |
| // assertTrue("No response for forwarding for reason " + callForwardingReasonToEnable, |
| // latch.await(TIMEOUT_FOR_NETWORK_OPS * 3, TimeUnit.MILLISECONDS)); |
| } |
| |
| // Disable Call Forwarding |
| for (int callForwardingReasonToDisable : callForwardingReasons) { |
| CountDownLatch latch = new CountDownLatch(1); |
| // Disregard success or failure; just make sure it reports back. |
| Consumer<Integer> ignoringResultListener = (x) -> latch.countDown(); |
| |
| final CallForwardingInfo callForwardingInfoToDisable = new CallForwardingInfo( |
| false, |
| callForwardingReasonToDisable, |
| TEST_FORWARD_NUMBER, |
| // time seconds |
| 1); |
| Log.d(TAG, "[testSetCallForwarding] Disable Call Forwarding. Reason: " |
| + callForwardingReasonToDisable + " Number: " + TEST_FORWARD_NUMBER |
| + " Time Seconds: 1"); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setCallForwarding(callForwardingInfoToDisable, |
| getContext().getMainExecutor(), ignoringResultListener)); |
| // TODO: this takes way too long on a real network (upwards of 40s). |
| //assertTrue("No response for forwarding for reason " + callForwardingReasonToDisable, |
| // latch.await(TIMEOUT_FOR_NETWORK_OPS * 3, TimeUnit.MILLISECONDS)); |
| } |
| } |
| |
| @Test |
| public void testGetCallWaitingStatus() throws Exception { |
| if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { |
| Log.d(TAG, "skipping test on device without FEATURE_TELEPHONY present"); |
| return; |
| } |
| Set<Integer> validCallWaitingStatuses = new HashSet<Integer>(); |
| validCallWaitingStatuses.add(TelephonyManager.CALL_WAITING_STATUS_ENABLED); |
| validCallWaitingStatuses.add(TelephonyManager.CALL_WAITING_STATUS_DISABLED); |
| validCallWaitingStatuses.add(TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR); |
| validCallWaitingStatuses.add(TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED); |
| validCallWaitingStatuses.add(TelephonyManager.CALL_WAITING_STATUS_FDN_CHECK_FAILURE); |
| |
| LinkedBlockingQueue<Integer> callWaitingStatusResult = new LinkedBlockingQueue<>(1); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, (tm) -> tm.getCallWaitingStatus(getContext().getMainExecutor(), |
| callWaitingStatusResult::offer)); |
| assertTrue(validCallWaitingStatuses.contains( |
| callWaitingStatusResult.poll(TIMEOUT_FOR_NETWORK_OPS, TimeUnit.MILLISECONDS))); |
| } |
| |
| @Test |
| public void testSetCallWaitingStatus() throws Exception { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_CALLING)); |
| |
| Set<Integer> validCallWaitingErrors = new HashSet<Integer>(); |
| validCallWaitingErrors.add(TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR); |
| validCallWaitingErrors.add(TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED); |
| validCallWaitingErrors.add(TelephonyManager.CALL_WAITING_STATUS_FDN_CHECK_FAILURE); |
| Executor executor = getContext().getMainExecutor(); |
| { |
| LinkedBlockingQueue<Integer> callWaitingResult = new LinkedBlockingQueue<>(1); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setCallWaitingEnabled(true, executor, callWaitingResult::offer)); |
| Integer result = callWaitingResult.poll(TIMEOUT_FOR_NETWORK_OPS, TimeUnit.MILLISECONDS); |
| assertNotNull("Never got callback from set call waiting", result); |
| if (result != TelephonyManager.CALL_WAITING_STATUS_ENABLED) { |
| assertTrue("Call waiting callback got an invalid value: " + result, |
| validCallWaitingErrors.contains(result)); |
| } |
| } |
| |
| { |
| LinkedBlockingQueue<Integer> callWaitingResult = new LinkedBlockingQueue<>(1); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setCallWaitingEnabled(false, executor, callWaitingResult::offer)); |
| Integer result = callWaitingResult.poll(TIMEOUT_FOR_NETWORK_OPS, TimeUnit.MILLISECONDS); |
| assertNotNull("Never got callback from set call waiting", result); |
| if (result != TelephonyManager.CALL_WAITING_STATUS_DISABLED) { |
| assertTrue("Call waiting callback got an invalid value: " + result, |
| validCallWaitingErrors.contains(result)); |
| } |
| } |
| } |
| |
| @Test |
| public void testGetHalVersion() { |
| Pair<Integer, Integer> halversion; |
| for (int i = TelephonyManager.HAL_SERVICE_DATA; |
| i <= TelephonyManager.HAL_SERVICE_VOICE; i++) { |
| halversion = mTelephonyManager.getHalVersion(i); |
| |
| // The version must be valid, and the versions start with 1.0 |
| assertFalse("Invalid HAL Version (" + halversion + ") of service (" + i + ")", |
| halversion.first < 1 || halversion.second < 0); |
| } |
| } |
| |
| @Test |
| public void testCreateForPhoneAccountHandle() { |
| if (!mTelephonyManager.isVoiceCapable()) { |
| Log.d(TAG, "Skipping test that requires config_voice_capable is true"); |
| return; |
| } |
| int subId = SubscriptionManager.getDefaultDataSubscriptionId(); |
| if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { |
| Log.d(TAG, "Skipping test that requires DefaultDataSubscriptionId setting"); |
| return; |
| } |
| |
| TelecomManager telecomManager = getContext().getSystemService(TelecomManager.class); |
| PhoneAccountHandle handle = |
| telecomManager.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL); |
| TelephonyManager telephonyManager = mTelephonyManager.createForPhoneAccountHandle(handle); |
| String globalSubscriberId = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.getSubscriberId()); |
| String localSubscriberId = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| telephonyManager, (tm) -> tm.getSubscriberId()); |
| assertEquals(globalSubscriberId, localSubscriberId); |
| } |
| |
| @Test |
| public void testCreateForPhoneAccountHandle_InvalidHandle(){ |
| PhoneAccountHandle handle = |
| new PhoneAccountHandle(new ComponentName("com.example.foo", "bar"), "baz"); |
| assertNull(mTelephonyManager.createForPhoneAccountHandle(handle)); |
| } |
| |
| @Test |
| @ApiTest(apis = {"android.telephony.TelephonyManager#getPhoneAccountHandle"}) |
| public void testGetPhoneAccountHandle() { |
| TelecomManager telecomManager = getContext().getSystemService(TelecomManager.class); |
| List<PhoneAccountHandle> callCapableAccounts = telecomManager |
| .getCallCapablePhoneAccounts(); |
| try { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .adoptShellPermissionIdentity( |
| android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE); |
| PhoneAccountHandle phoneAccountHandle = mTelephonyManager.getPhoneAccountHandle(); |
| assertTrue(callCapableAccounts.contains(phoneAccountHandle)); |
| } catch (SecurityException e) { |
| fail("TelephonyManager#getPhoneAccountHandle requires READ_PRIVILEGED_PHONE_STATE"); |
| } finally { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .dropShellPermissionIdentity(); |
| } |
| } |
| |
| /** |
| * Tests that the phone count returned is valid. |
| */ |
| @Test |
| public void testGetPhoneCount() { |
| int phoneCount = mTelephonyManager.getPhoneCount(); |
| int phoneType = mTelephonyManager.getPhoneType(); |
| switch (phoneType) { |
| case TelephonyManager.PHONE_TYPE_GSM: |
| case TelephonyManager.PHONE_TYPE_CDMA: |
| assertTrue("Phone count should be > 0", phoneCount > 0); |
| break; |
| case TelephonyManager.PHONE_TYPE_NONE: |
| assertTrue("Phone count should be >= 0", phoneCount >= 0); |
| break; |
| default: |
| throw new IllegalArgumentException("Did you add a new phone type? " + phoneType); |
| } |
| } |
| |
| /** |
| * Tests that the device properly reports either a valid IMEI, MEID/ESN, or a valid MAC address |
| * if only a WiFi device. At least one of them must be valid. |
| */ |
| @Test |
| public void testGetDeviceId() { |
| String deviceId = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getDeviceId()); |
| verifyDeviceId(deviceId); |
| } |
| |
| /** |
| * Tests the max number of active SIMs method |
| */ |
| @Test |
| public void testGetMaxNumberOfSimultaneouslyActiveSims() { |
| int maxNum = mTelephonyManager.getMaxNumberOfSimultaneouslyActiveSims(); |
| assertTrue(maxNum >= 1); |
| } |
| |
| /** |
| * Tests that the device properly reports either a valid IMEI, MEID/ESN, or a valid MAC address |
| * if only a WiFi device. At least one of them must be valid. |
| */ |
| @Test |
| public void testGetDeviceIdForSlot() { |
| String deviceId = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getDeviceId(mTelephonyManager.getSlotIndex())); |
| verifyDeviceId(deviceId); |
| // Also verify that no exception is thrown for any slot index (including invalid ones) |
| for (int i = -1; i <= mTelephonyManager.getPhoneCount(); i++) { |
| // The compiler error 'local variables referenced from a lambda expression must be final |
| // or effectively final' is reported when using i, so assign it to a final variable. |
| final int currI = i; |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getDeviceId(currI)); |
| } |
| } |
| |
| private void verifyDeviceId(String deviceId) { |
| if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { |
| // Either IMEI or MEID need to be valid. |
| try { |
| assertImei(deviceId); |
| } catch (AssertionError e) { |
| assertMeidEsn(deviceId); |
| } |
| } else if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) { |
| assertSerialNumber(); |
| assertMacAddress(getWifiMacAddress()); |
| } else if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) { |
| assertSerialNumber(); |
| assertMacAddress(getBluetoothMacAddress()); |
| } else if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET)) { |
| assertTrue(mCm.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET) != null); |
| } |
| } |
| |
| private static void assertImei(String id) { |
| assertFalse("Imei should not be empty or null", TextUtils.isEmpty(id)); |
| // IMEI must have 15 digits. |
| String imeiPattern = "[0-9]{15}"; |
| String invalidPattern = "[0]{15}"; |
| assertTrue("IMEI " + id + " does not match pattern " + imeiPattern, |
| Pattern.matches(imeiPattern, id)); |
| assertFalse("IMEI " + id + " must not be a zero sequence" + invalidPattern, |
| Pattern.matches(invalidPattern, id)); |
| // 15th digit must be a check digit. |
| assertImeiCheckDigit(id); |
| } |
| |
| private static void assertImeiCheckDigit(String deviceId) { |
| int expectedCheckDigit = getLuhnCheckDigit(deviceId.substring(0, 14)); |
| int actualCheckDigit = Character.digit(deviceId.charAt(14), 10); |
| assertEquals("Incorrect check digit for " + deviceId, expectedCheckDigit, actualCheckDigit); |
| } |
| |
| /** |
| * Use decimal value (0-9) to index into array to get sum of its digits |
| * needed by Lunh check. |
| * |
| * Example: DOUBLE_DIGIT_SUM[6] = 3 because 6 * 2 = 12 => 1 + 2 = 3 |
| */ |
| private static final int[] DOUBLE_DIGIT_SUM = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9}; |
| |
| /** |
| * Calculate the check digit by starting from the right, doubling every |
| * each digit, summing all the digits including the doubled ones, and |
| * finding a number to make the sum divisible by 10. |
| * |
| * @param deviceId not including the check digit |
| * @return the check digit |
| */ |
| private static int getLuhnCheckDigit(String deviceId) { |
| int sum = 0; |
| int dontDoubleModulus = deviceId.length() % 2; |
| for (int i = deviceId.length() - 1; i >= 0; --i) { |
| int digit = Character.digit(deviceId.charAt(i), 10); |
| if (i % 2 == dontDoubleModulus) { |
| sum += digit; |
| } else { |
| sum += DOUBLE_DIGIT_SUM[digit]; |
| } |
| } |
| sum %= 10; |
| return sum == 0 ? 0 : 10 - sum; |
| } |
| |
| private static void assertMeidEsn(String id) { |
| // CDMA device IDs may either be a 14-hex-digit MEID or an |
| // 8-hex-digit ESN. If it's an ESN, it may not be a |
| // pseudo-ESN. |
| assertFalse("Meid ESN should not be empty or null", TextUtils.isEmpty(id)); |
| if (id.length() == 14) { |
| assertMeidFormat(id); |
| } else if (id.length() == 8) { |
| assertHexadecimalEsnFormat(id); |
| } else { |
| fail("device id on CDMA must be 14-digit hex MEID or 8-digit hex ESN."); |
| } |
| } |
| |
| private static void assertHexadecimalEsnFormat(String deviceId) { |
| String esnPattern = "[0-9a-fA-F]{8}"; |
| String invalidPattern = "[0]{8}"; |
| assertTrue("ESN hex device id " + deviceId + " does not match pattern " + esnPattern, |
| Pattern.matches(esnPattern, deviceId)); |
| assertFalse("ESN hex device id " + deviceId + " must not be a pseudo-ESN", |
| "80".equals(deviceId.substring(0, 2))); |
| assertFalse("ESN hex device id " + deviceId + "must not be a zero sequence", |
| Pattern.matches(invalidPattern, deviceId)); |
| } |
| |
| private static void assertMeidFormat(String deviceId) { |
| // MEID must NOT include the check digit. |
| String meidPattern = "[0-9a-fA-F]{14}"; |
| String invalidPattern = "[0]{14}"; |
| assertTrue("MEID device id " + deviceId + " does not match pattern " |
| + meidPattern, Pattern.matches(meidPattern, deviceId)); |
| assertFalse("MEID device id " + deviceId + "must not be a zero sequence", |
| Pattern.matches(invalidPattern, deviceId)); |
| } |
| |
| private void assertSerialNumber() { |
| String serial = ShellIdentityUtils.invokeStaticMethodWithShellPermissions( |
| Build::getSerial); |
| assertNotNull("Non-telephony devices must have a Build.getSerial() number.", |
| serial); |
| assertTrue("Hardware id must be alphanumeric.", |
| Pattern.matches("[0-9A-Za-z.,_-]+", serial)); |
| } |
| |
| private void assertMacAddress(String macAddress) { |
| String macPattern = "([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}"; |
| assertTrue("MAC Address " + macAddress + " does not match pattern " + macPattern, |
| Pattern.matches(macPattern, macAddress)); |
| } |
| |
| /** @return mac address which requires the WiFi system to be enabled */ |
| private String getWifiMacAddress() { |
| WifiManager wifiManager = getContext().getSystemService(WifiManager.class); |
| |
| if (wifiManager.isWifiEnabled()) { |
| return wifiManager.getConnectionInfo().getMacAddress(); |
| } else { |
| try { |
| runWithShellPermissionIdentity(() -> wifiManager.setWifiEnabled(true)); |
| |
| return wifiManager.getConnectionInfo().getMacAddress(); |
| |
| } finally { |
| runWithShellPermissionIdentity(() -> wifiManager.setWifiEnabled(false)); |
| } |
| } |
| } |
| |
| private String getBluetoothMacAddress() { |
| BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); |
| if (adapter == null) { |
| return ""; |
| } |
| |
| return adapter.getAddress(); |
| } |
| |
| private static final String ISO_COUNTRY_CODE_PATTERN = "[a-z]{2}"; |
| |
| @Test |
| @ApiTest(apis = "android.telephony.TelephonyManager#getNetworkCountryIso") |
| public void testGetNetworkCountryIso() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| String countryCode = mTelephonyManager.getNetworkCountryIso(); |
| ServiceState serviceState = mTelephonyManager.getServiceState(); |
| if (serviceState != null && (serviceState.getState() |
| == ServiceState.STATE_IN_SERVICE || serviceState.getState() |
| == ServiceState.STATE_EMERGENCY_ONLY)) { |
| assertTrue("Country code '" + countryCode + "' did not match " |
| + ISO_COUNTRY_CODE_PATTERN, |
| Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode)); |
| } else { |
| assertTrue("Country code could be empty when out of service", |
| Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode) |
| || TextUtils.isEmpty(countryCode)); |
| } |
| |
| int[] allSubs = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mSubscriptionManager, (sm) -> sm.getActiveSubscriptionIdList()); |
| for (int i : allSubs) { |
| countryCode = mTelephonyManager.getNetworkCountryIso( |
| SubscriptionManager.getSlotIndex(i)); |
| serviceState = mTelephonyManager.createForSubscriptionId(i).getServiceState(); |
| |
| if (serviceState != null && (serviceState.getState() |
| == ServiceState.STATE_IN_SERVICE || serviceState.getState() |
| == ServiceState.STATE_EMERGENCY_ONLY)) { |
| assertTrue("Country code '" + countryCode + "' did not match " |
| + ISO_COUNTRY_CODE_PATTERN + " for slot " + i, |
| Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode)); |
| } else { |
| assertTrue("Country code could be empty when out of service", |
| Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode) |
| || TextUtils.isEmpty(countryCode)); |
| } |
| } |
| |
| for (int i = 0; i < mTelephonyManager.getPhoneCount(); i++) { |
| countryCode = mTelephonyManager.getNetworkCountryIso(i); |
| assertTrue("Country code must match " + ISO_COUNTRY_CODE_PATTERN + "or empty", |
| Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode) |
| || TextUtils.isEmpty(countryCode)); |
| } |
| } |
| |
| @Test |
| public void testSetSystemSelectionChannels() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // Get initial list of system selection channels if the API is available |
| List<RadioAccessSpecifier> initialSpecifiers = tryGetSystemSelectionChannels(); |
| // TODO (b/189255895): Don't allow empty or null channels once API is enforced in U. |
| boolean getAvailable = initialSpecifiers != null && !initialSpecifiers.isEmpty(); |
| Log.d(TAG, "getSystemSelectionChannels is " + (getAvailable ? "" : "not ") + "available."); |
| |
| List<RadioAccessSpecifier> validSpecifiers = new ArrayList<>(); |
| List<RadioAccessSpecifier> specifiers; |
| for (int accessNetworkType : TelephonyUtils.ALL_BANDS.keySet()) { |
| List<Integer> validBands = new ArrayList<>(); |
| for (int band : TelephonyUtils.ALL_BANDS.get(accessNetworkType)) { |
| // Set each band to see which ones are supported by the modem |
| RadioAccessSpecifier specifier = new RadioAccessSpecifier( |
| accessNetworkType, new int[]{band}, new int[]{}); |
| boolean success = trySetSystemSelectionChannels( |
| Collections.singletonList(specifier), true); |
| if (success) { |
| validBands.add(band); |
| |
| // Try calling the API that doesn't provide feedback. |
| // We have no way of knowing if it succeeds, so just make sure nothing crashes. |
| trySetSystemSelectionChannels(Collections.singletonList(specifier), false); |
| |
| if (getAvailable) { |
| // Assert that we get back the value we set. |
| specifiers = tryGetSystemSelectionChannels(); |
| assertNotNull(specifiers); |
| assertEquals(1, specifiers.size()); |
| assertEquals(specifier, specifiers.get(0)); |
| } |
| } |
| } |
| if (!validBands.isEmpty()) { |
| validSpecifiers.add(new RadioAccessSpecifier(accessNetworkType, |
| validBands.stream().mapToInt(i -> i).toArray(), new int[]{})); |
| } |
| } |
| |
| // Call setSystemSelectionChannels with an empty list and verify no error |
| if (!trySetSystemSelectionChannels(Collections.emptyList(), true)) { |
| // TODO (b/189255895): Reset initial system selection channels on failure |
| fail("Failed to call setSystemSelectionChannels with an empty list."); |
| } |
| |
| // Verify that getSystemSelectionChannels returns all valid specifiers |
| specifiers = tryGetSystemSelectionChannels(); |
| // TODO (b/189255895): Uncomment in U after getSystemSelectionChannels is enforced |
| //assertNotNull(specifiers); |
| //assertEquals(specifiers.size(), validSpecifiers.size()); |
| //assertTrue(specifiers.containsAll(validSpecifiers)); |
| |
| // Call setSystemSelectionChannels with all valid specifiers to test batch operations |
| if (!trySetSystemSelectionChannels(validSpecifiers, true)) { |
| // TODO (b/189255895): Reset initial system selection channels on failure |
| // TODO (b/189255895): Fail once setSystemSelectionChannels is enforced properly |
| Log.e(TAG, "Failed to call setSystemSelectionChannels with all valid specifiers."); |
| } |
| |
| // Reset the values back to the original. |
| if (getAvailable) { |
| trySetSystemSelectionChannels(initialSpecifiers, true); |
| } |
| } |
| |
| private List<RadioAccessSpecifier> tryGetSystemSelectionChannels() { |
| UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); |
| uiAutomation.adoptShellPermissionIdentity(); |
| List<RadioAccessSpecifier> channels = null; |
| try { |
| channels = mTelephonyManager.getSystemSelectionChannels(); |
| } catch (IllegalStateException ignored) { |
| // TODO (b/189255895): Reset and fail in U after getSystemSelectionChannels is enforced |
| } finally { |
| uiAutomation.dropShellPermissionIdentity(); |
| } |
| return channels; |
| } |
| |
| private boolean trySetSystemSelectionChannels(List<RadioAccessSpecifier> specifiers, |
| boolean useCallback) { |
| UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); |
| uiAutomation.adoptShellPermissionIdentity(); |
| boolean success = false; |
| try { |
| if (useCallback) { |
| LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue<>(1); |
| // This is a oneway binder call, meaning we may return before the permission check |
| // happens. Hold shell permissions until we get a response. |
| mTelephonyManager.setSystemSelectionChannels( |
| specifiers, getContext().getMainExecutor(), queue::offer); |
| Boolean result = queue.poll(2000, TimeUnit.MILLISECONDS); |
| |
| // Ensure we get a result |
| assertNotNull(result); |
| success = result; |
| } else { |
| mTelephonyManager.setSystemSelectionChannels(specifiers); |
| success = true; |
| } |
| } catch (InterruptedException e) { |
| // TODO (b/189255895): Reset initial system selection channels on failure |
| fail("setSystemSelectionChannels interrupted."); |
| } finally { |
| uiAutomation.dropShellPermissionIdentity(); |
| } |
| return success; |
| } |
| |
| @Test |
| public void testGetSimCountryIso() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| String countryCode = mTelephonyManager.getSimCountryIso(); |
| if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) && |
| !countryCode.isEmpty()) { |
| assertTrue("Country code '" + countryCode + "' did not match " |
| + ISO_COUNTRY_CODE_PATTERN, |
| Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode)); |
| } else { |
| // Non-telephony may still have the property defined if it has a SIM. |
| } |
| } |
| |
| @Test |
| public void testResetSettings() throws Exception { |
| UserManager userManager = getContext().getSystemService(UserManager.class); |
| |
| boolean canChangeMobileNetworkSettings = userManager != null |
| && !userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); |
| assertTrue("Primary user must be able to configure mobile networks to pass this test", |
| canChangeMobileNetworkSettings); |
| boolean initialDataSetting = isDataEnabled(); |
| |
| //First check permissions are correct |
| try { |
| mTelephonyManager.resetSettings(); |
| fail("TelephonyManager#resetSettings requires the" |
| + " android.Manifest.permission.NETWORK_SETTINGS permission"); |
| } catch (SecurityException e) { |
| //expected |
| } |
| // and then do a reset to move data to default. |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| TelephonyManager::resetSettings, |
| android.Manifest.permission.NETWORK_SETTINGS, |
| android.Manifest.permission.MODIFY_PHONE_STATE); |
| } catch (SecurityException e) { |
| e.printStackTrace(); |
| fail(e.toString()); |
| } |
| // This may timeout because the default is equal to the initial data setting, but there is |
| // no way to definitively check what the default should be, so assume the default will be |
| // set within TOLERANCE time. |
| TelephonyUtils.pollUntilTrue(() -> initialDataSetting != isDataEnabled(), 5 /*times*/, |
| TOLERANCE/5 /*timeout per poll*/); |
| |
| boolean defaultDataSetting = isDataEnabled(); |
| |
| // set data to not the default! |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setDataEnabled(!defaultDataSetting)); |
| assertTrue("Data enable change didn't work", |
| TelephonyUtils.pollUntilTrue(() -> defaultDataSetting != isDataEnabled(), |
| 5 /*times*/, TOLERANCE/5 /*timeout per poll*/)); |
| |
| // and then do a reset to move data to default again. |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| TelephonyManager::resetSettings, |
| android.Manifest.permission.NETWORK_SETTINGS, |
| android.Manifest.permission.MODIFY_PHONE_STATE); |
| } catch (SecurityException e) { |
| e.printStackTrace(); |
| fail(e.toString()); |
| } |
| |
| assertTrue("resetSettings did not reset default data", |
| TelephonyUtils.pollUntilTrue(() -> defaultDataSetting == isDataEnabled(), |
| 5 /*times*/, TOLERANCE/5 /*timeout per poll*/)); |
| } |
| |
| @Test |
| public void testNetworkTypeMatchesDataNetworkType() throws Exception { |
| assertEquals(mTelephonyManager.getDataNetworkType(), |
| mTelephonyManager.getNetworkType()); |
| } |
| |
| @Test |
| public void testNetworkTypeMatchesCellIdentity() throws Exception { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| ServiceState ss = mTelephonyManager.getServiceState(); |
| assertNotNull(ss); |
| for (NetworkRegistrationInfo nri : ss.getNetworkRegistrationInfoList()) { |
| final int networkType = nri.getAccessNetworkTechnology(); |
| final CellIdentity cid = nri.getCellIdentity(); |
| if (nri.getTransportType() == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { |
| assertTrue("NetworkType for WLAN transport must be IWLAN if registered or" |
| + " UNKNOWN if unregistered", |
| networkType == TelephonyManager.NETWORK_TYPE_UNKNOWN |
| || networkType == TelephonyManager.NETWORK_TYPE_IWLAN); |
| assertNull("There is no valid cell type for WLAN", cid); |
| continue; |
| } |
| if (!nri.isRegistered() && !nri.isEmergencyEnabled()) { |
| assertEquals( |
| "Network type cannot be known unless it is providing some service", |
| TelephonyManager.NETWORK_TYPE_UNKNOWN, networkType); |
| assertNull(cid); |
| continue; |
| } |
| |
| assertEquals(nri.getTransportType(), AccessNetworkConstants.TRANSPORT_TYPE_WWAN); |
| if (nri.isRegistered() || (nri.isEmergencyEnabled() && !nri.isSearching())) { |
| assertNotEquals("Network type must be known if it is providing some service", |
| TelephonyManager.NETWORK_TYPE_UNKNOWN, networkType); |
| assertNotNull("The cid must be known for a cell providing service", cid); |
| // The network type must roughly match the CellIdentity type |
| assertTrue("The network type must be valid for the current cell", |
| sNetworkTypes.get(cid.getClass()).contains(networkType)); |
| } |
| } |
| } |
| |
| @Test |
| public void testGetServiceState() throws InterruptedException { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) { |
| Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE"); |
| return; |
| } |
| |
| TestThread t = new TestThread(() -> { |
| Looper.prepare(); |
| |
| mListener = new PhoneStateListener() { |
| @Override |
| public void onServiceStateChanged(ServiceState serviceState) { |
| synchronized (mLock) { |
| mServiceState = serviceState; |
| mLock.notify(); |
| } |
| } |
| }; |
| mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_SERVICE_STATE); |
| Looper.loop(); |
| }); |
| |
| synchronized (mLock) { |
| t.start(); |
| mLock.wait(TOLERANCE); |
| } |
| |
| // Service state changes frequently and there can be a mismatch between the current service |
| // state from TelephonyManager and the slightly delayed one from the listener. |
| // Retry all assertions multiple times to prevent flaky test failures. |
| int retries = 5; |
| for (int i = 0; i < retries; i++) { |
| try { |
| assertEquals(mServiceState, mTelephonyManager.getServiceState()); |
| // Exit if the assertion passes without an exception |
| break; |
| } catch (AssertionError e) { |
| if (i == retries - 1) { |
| throw(e); |
| } |
| } |
| waitForMs(100); |
| } |
| for (int i = 0; i < retries; i++) { |
| try { |
| assertServiceStateSanitization(mServiceState, |
| mTelephonyManager.getServiceState( |
| TelephonyManager.INCLUDE_LOCATION_DATA_NONE)); |
| // Exit if the assertion passes without an exception |
| break; |
| } catch (AssertionError e) { |
| if (i == retries - 1) { |
| throw(e); |
| } |
| } |
| waitForMs(100); |
| } |
| for (int i = 0; i < retries; i++) { |
| try { |
| assertServiceStateFineLocationSanitization(mServiceState, |
| mTelephonyManager.getServiceState( |
| TelephonyManager.INCLUDE_LOCATION_DATA_COARSE)); |
| // Exit if the assertion passes without an exception |
| break; |
| } catch (AssertionError e) { |
| if (i == retries - 1) { |
| throw(e); |
| } |
| } |
| waitForMs(100); |
| } |
| for (int i = 0; i < retries; i++) { |
| try { |
| assertEquals(mServiceState, |
| mTelephonyManager.getServiceState( |
| TelephonyManager.INCLUDE_LOCATION_DATA_FINE)); |
| // Exit if the assertion passes without an exception |
| break; |
| } catch (AssertionError e) { |
| if (i == retries - 1) { |
| throw(e); |
| } |
| } |
| waitForMs(100); |
| } |
| |
| NetworkRegistrationInfo regInfo = mServiceState.getNetworkRegistrationInfo( |
| NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); |
| if (regInfo != null) { |
| DataSpecificRegistrationInfo dsri = regInfo.getDataSpecificInfo(); |
| if (dsri != null) { |
| int lteAttachType = dsri.getLteAttachResultType(); |
| assertTrue(lteAttachType == LTE_ATTACH_TYPE_UNKNOWN |
| || lteAttachType == LTE_ATTACH_TYPE_EPS_ONLY |
| || lteAttachType == LTE_ATTACH_TYPE_COMBINED); |
| |
| int lteAttachExtraInfo = dsri.getLteAttachExtraInfo(); |
| assertTrue(lteAttachExtraInfo == LTE_ATTACH_EXTRA_INFO_NONE |
| || lteAttachExtraInfo == LTE_ATTACH_EXTRA_INFO_SMS_ONLY |
| || lteAttachExtraInfo == LTE_ATTACH_EXTRA_INFO_CSFB_NOT_PREFERRED |
| || lteAttachExtraInfo == (LTE_ATTACH_EXTRA_INFO_SMS_ONLY |
| | LTE_ATTACH_EXTRA_INFO_CSFB_NOT_PREFERRED)); |
| } |
| } |
| } |
| |
| private void assertServiceStateSanitization(ServiceState expectedServiceState, |
| ServiceState receivedServiceState) { |
| assertNotEquals(null, receivedServiceState); |
| assertServiceStateFineLocationSanitization(expectedServiceState, receivedServiceState); |
| |
| assertTrue(TextUtils.isEmpty(receivedServiceState.getOperatorAlphaLong())); |
| assertTrue(TextUtils.isEmpty(receivedServiceState.getOperatorAlphaShort())); |
| assertTrue(TextUtils.isEmpty(receivedServiceState.getOperatorNumeric())); |
| } |
| |
| private void assertServiceStateFineLocationSanitization(ServiceState expectedServiceState, |
| ServiceState receivedServiceState) { |
| assertNotEquals(null, receivedServiceState); |
| |
| assertEquals(expectedServiceState.getVoiceRegState(), |
| receivedServiceState.getVoiceRegState()); |
| assertEquals(expectedServiceState.getDataRegState(), |
| receivedServiceState.getDataRegState()); |
| assertEquals(expectedServiceState.getDataNetworkType(), |
| receivedServiceState.getDataNetworkType()); |
| assertEquals(expectedServiceState.getDataRoaming(), |
| receivedServiceState.getDataRoaming()); |
| assertEquals(expectedServiceState.getRilVoiceRadioTechnology(), |
| receivedServiceState.getRilVoiceRadioTechnology()); |
| |
| if (receivedServiceState.getNetworkRegistrationInfoList() != null) { |
| for (NetworkRegistrationInfo nrs : receivedServiceState |
| .getNetworkRegistrationInfoList()) { |
| assertNull(nrs.getCellIdentity()); |
| } |
| } |
| } |
| |
| @Test |
| public void testGetServiceStateForInactiveSub() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) { |
| Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE"); |
| return; |
| } |
| |
| int[] allSubs = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mSubscriptionManager, (sm) ->sm.getActiveSubscriptionIdList()); |
| // generate a subscription that is valid (>0) but inactive (not part of active subId list) |
| // A simple way to do this is sum the active subIds and add 1 |
| int inactiveValidSub = 1; |
| for (int sub : allSubs) { |
| inactiveValidSub += sub; |
| } |
| |
| assertNull(mTelephonyManager.createForSubscriptionId(inactiveValidSub).getServiceState()); |
| } |
| |
| // This test is to ensure the RAT IWLAN is not reported on WWAN transport if the device is |
| // operated in AP-assisted mode. |
| @Test |
| @CddTest(requirement = "7.4.1/C-4-1") |
| public void testIWlanServiceState() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) { |
| Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE"); |
| return; |
| } |
| String mode = SystemProperties.get("ro.telephony.iwlan_operation_mode"); |
| if (!mode.equals("legacy")) { |
| ServiceState ss = mTelephonyManager.getServiceState(); |
| if (ss != null) { |
| for (NetworkRegistrationInfo nri : ss.getNetworkRegistrationInfoList()) { |
| if (nri.getTransportType() == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { |
| assertNotEquals(TelephonyManager.NETWORK_TYPE_IWLAN, |
| nri.getAccessNetworkTechnology()); |
| } |
| } |
| } |
| } |
| } |
| |
| private MockPhoneCapabilityListener mMockPhoneCapabilityListener; |
| |
| private class MockPhoneCapabilityListener extends TelephonyCallback |
| implements TelephonyCallback.PhoneCapabilityListener { |
| @Override |
| public void onPhoneCapabilityChanged(PhoneCapability capability) { |
| synchronized (mLock) { |
| mPhoneCapability = capability; |
| mOnPhoneCapabilityChanged = true; |
| mLock.notify(); |
| } |
| } |
| } |
| |
| @Test |
| public void testGetPhoneCapabilityAndVerify() { |
| boolean is5gStandalone = getContext().getResources().getBoolean( |
| Resources.getSystem().getIdentifier("config_telephony5gStandalone", "bool", |
| "android")); |
| boolean is5gNonStandalone = getContext().getResources().getBoolean( |
| Resources.getSystem().getIdentifier("config_telephony5gNonStandalone", "bool", |
| "android")); |
| int[] deviceNrCapabilities = new int[0]; |
| if (is5gStandalone || is5gNonStandalone) { |
| List<Integer> list = new ArrayList<>(); |
| if (is5gNonStandalone) { |
| list.add(DEVICE_NR_CAPABILITY_NSA); |
| } |
| if (is5gStandalone) { |
| list.add(DEVICE_NR_CAPABILITY_SA); |
| } |
| deviceNrCapabilities = list.stream().mapToInt(Integer::valueOf).toArray(); |
| } |
| |
| PhoneCapability phoneCapability = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.getPhoneCapability()); |
| |
| assertArrayEquals(deviceNrCapabilities, phoneCapability.getDeviceNrCapabilities()); |
| } |
| |
| @Test |
| public void testGetSimLocale() throws InterruptedException { |
| if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { |
| Log.d(TAG,"skipping test that requires Telephony"); |
| return; |
| } |
| if (SubscriptionManager.getDefaultSubscriptionId() |
| == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { |
| fail("Expected SIM inserted"); |
| } |
| Locale locale = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getSimLocale()); |
| Log.d(TAG, "testGetSimLocale: " + locale); |
| assertNotNull(locale); |
| } |
| |
| /** |
| * Tests that a GSM device properly reports either the correct TAC (type allocation code) or |
| * null. |
| * The TAC should match the first 8 digits of the IMEI. |
| */ |
| @Test |
| public void testGetTac() { |
| String tac = mTelephonyManager.getTypeAllocationCode(); |
| String imei = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getImei()); |
| |
| if (tac == null || imei == null) { |
| return; |
| } |
| |
| if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { |
| if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) { |
| assertEquals(imei.substring(0, 8), tac); |
| } |
| } |
| } |
| |
| /** |
| * Tests that a CDMA device properly reports either the correct MC (manufacturer code) or null. |
| * The MC should match the first 8 digits of the MEID. |
| */ |
| @Test |
| public void testGetMc() { |
| String mc = mTelephonyManager.getManufacturerCode(); |
| String meid = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getMeid()); |
| |
| if (mc == null || meid == null) { |
| return; |
| } |
| |
| // mc and meid should either be null or supported. empty string is not expected even if |
| // the device does not support mc/meid. |
| assertNotEquals("", mc); |
| assertNotEquals("", meid); |
| |
| if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { |
| if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { |
| assertEquals(meid.substring(0, 8), mc); |
| } |
| } |
| } |
| |
| /** |
| * Tests that the device properly reports either a valid IMEI or null. |
| */ |
| @Test |
| public void testGetImei() { |
| String imei = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getImei()); |
| |
| if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { |
| if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) { |
| assertImei(imei); |
| } |
| } |
| } |
| |
| /** |
| * Tests that the device properly reports either a valid Primary Imei. |
| */ |
| @Test |
| public void testGetPrimaryImei() { |
| // make sure the modem supports primaryImei feature |
| assumeTrue(mModemHalVersion >= RADIO_HAL_VERSION_2_1); |
| String primaryImei = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getPrimaryImei()); |
| |
| if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { |
| if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) { |
| assertImei(primaryImei); |
| } |
| } |
| } |
| |
| /** |
| * Tests that the device properly reports either a valid IMEI or null. |
| */ |
| @Test |
| public void testGetImeiForSlot() { |
| for (int i = 0; i < mTelephonyManager.getPhoneCount(); i++) { |
| // The compiler error 'local variables referenced from a lambda expression must be final |
| // or effectively final' is reported when using i, so assign it to a final variable. |
| final int currI = i; |
| String imei = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getImei(currI)); |
| if (!TextUtils.isEmpty(imei)) { |
| assertImei(imei); |
| } |
| } |
| |
| // Also verify that no exception is thrown for any slot index (including invalid ones) |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getImei(-1)); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getImei(mTelephonyManager.getPhoneCount())); |
| } |
| |
| /** |
| * Verifies that {@link TelephonyManager#getRadioPowerState()} does not throw any exception |
| * and returns radio on. |
| */ |
| @Test |
| public void testGetRadioPowerState() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // Also verify that no exception is thrown. |
| assertThat(mTelephonyManager.getRadioPowerState()).isEqualTo( |
| TelephonyManager.RADIO_POWER_ON); |
| } |
| |
| /** |
| * Verifies that {@link TelephonyManager#setCarrierDataEnabled(boolean)} does not throw any |
| * exception. TODO enhance later if we have an API to get data enabled state. |
| */ |
| @Test |
| public void testSetCarrierDataEnabled() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_DATA)); |
| |
| // Also verify that no exception is thrown. |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setCarrierDataEnabled(false)); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setCarrierDataEnabled(true)); |
| } |
| |
| /** |
| * Verifies that {@link TelephonyManager#rebootModem()} does not throw any exception |
| * and final radio state is radio power on. |
| */ |
| @Test |
| public void testRebootRadio() throws Throwable { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| if (mModemHalVersion <= RADIO_HAL_VERSION_2_2) { |
| Log.d(TAG, |
| "Skipping test since rebootModem is not supported/enforced until IRadio 2.3."); |
| return; |
| } |
| |
| TestThread t = new TestThread(() -> { |
| Looper.prepare(); |
| |
| mListener = new PhoneStateListener() { |
| @Override |
| public void onRadioPowerStateChanged(@RadioPowerState int state) { |
| synchronized (mLock) { |
| if (state == TelephonyManager.RADIO_POWER_ON && mHasRadioPowerOff) { |
| mRadioRebootTriggered = true; |
| mLock.notify(); |
| } else if (state == TelephonyManager.RADIO_POWER_OFF) { |
| // reboot must go to power off |
| mHasRadioPowerOff = true; |
| } |
| } |
| } |
| }; |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.listen(mListener, |
| PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED)); |
| Looper.loop(); |
| }); |
| |
| assertThat(mTelephonyManager.getRadioPowerState()).isEqualTo( |
| TelephonyManager.RADIO_POWER_ON); |
| assertThat(mRadioRebootTriggered).isFalse(); |
| assertThat(mHasRadioPowerOff).isFalse(); |
| t.start(); |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| TelephonyManager::rebootModem); |
| } catch (Exception ex) { |
| //skip this test if not supported or unsuccessful (success=false) |
| return; |
| } |
| |
| synchronized (mLock) { |
| // reboot takes longer time |
| if (!mRadioRebootTriggered) { |
| mLock.wait(20000); |
| } |
| } |
| assertThat(mTelephonyManager.getRadioPowerState()).isEqualTo( |
| TelephonyManager.RADIO_POWER_ON); |
| assertThat(mRadioRebootTriggered).isTrue(); |
| |
| if (mListener != null) { |
| // unregister the listener |
| mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE); |
| } |
| |
| // note, other telephony states might not resumes properly at this point. e.g, service state |
| // might still in the transition from OOS to In service. Thus we need to wait for in |
| // service state before running next tests. |
| t = new TestThread(() -> { |
| Looper.prepare(); |
| |
| mListener = new PhoneStateListener() { |
| @Override |
| public void onServiceStateChanged(ServiceState serviceState) { |
| synchronized (mLock) { |
| if (serviceState.getState() == ServiceState.STATE_IN_SERVICE) { |
| mServiceStateChangedCalled = true; |
| mLock.notify(); |
| } |
| } |
| } |
| }; |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.listen(mListener, PhoneStateListener.LISTEN_SERVICE_STATE)); |
| Looper.loop(); |
| }); |
| |
| synchronized (mLock) { |
| t.start(); |
| if (!mServiceStateChangedCalled) { |
| mLock.wait(60000); |
| } |
| } |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .adoptShellPermissionIdentity(android.Manifest.permission.READ_PHONE_STATE); |
| assertThat(mTelephonyManager.getServiceState().getState()).isEqualTo( |
| ServiceState.STATE_IN_SERVICE); |
| } |
| |
| /** |
| * Verifies that {@link TelephonyManager#getAidForAppType(int)} does not throw any exception |
| * for all supported subscription app type. |
| */ |
| @Test |
| public void testGetAidForAppType() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getAidForAppType(TelephonyManager.APPTYPE_SIM)); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getAidForAppType(TelephonyManager.APPTYPE_CSIM)); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getAidForAppType(TelephonyManager.APPTYPE_RUIM)); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getAidForAppType(TelephonyManager.APPTYPE_ISIM)); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getAidForAppType(TelephonyManager.APPTYPE_USIM)); |
| } |
| |
| /** |
| * Verifies that {@link TelephonyManager#getIsimDomain()} does not throw any exception |
| */ |
| @Test |
| public void testGetIsimDomain() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getIsimDomain()); |
| } |
| |
| /** |
| * Verifies that {@link TelephonyManager#getIsimImpu()} does not throw any exception when called |
| * and has the correct permissions. |
| */ |
| @Ignore("API moved back to @hide for Android R.") |
| @Test |
| public void testGetIsimImpu() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| TelephonyManager::getIsimImpu); |
| // Try without the correct permissions and ensure it fails. |
| try { |
| mTelephonyManager.getIsimImpu(); |
| fail(); |
| } catch (SecurityException e) { |
| // expected |
| } |
| } |
| |
| /** |
| * Basic test to ensure {@link NetworkRegistrationInfo#getRegisteredPlmn()} provides valid |
| * information. |
| */ |
| @Test |
| public void testNetworkRegistrationInfoRegisteredPlmn() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // get NetworkRegistration object |
| ServiceState ss = mTelephonyManager.getServiceState(); |
| assertNotNull(ss); |
| |
| boolean hasRegistered = false; |
| for (NetworkRegistrationInfo nwReg : ss.getNetworkRegistrationInfoList()) { |
| if (nwReg.isRegistered() |
| && nwReg.getTransportType() == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { |
| hasRegistered = true; |
| String plmnId = nwReg.getRegisteredPlmn(); |
| // CDMA doesn't have PLMN IDs. Rather than put CID|NID here, instead it will be |
| // empty. It's a case that's becoming less important over time, but for now a |
| // device that's only registered on CDMA needs to pass this test. |
| if (nwReg.getCellIdentity() instanceof android.telephony.CellIdentityCdma) { |
| assertTrue(TextUtils.isEmpty(plmnId)); |
| } else { |
| assertFalse(TextUtils.isEmpty(plmnId)); |
| assertTrue("PlmnId() out of range [00000 - 999999], PLMN ID=" + plmnId, |
| plmnId.matches("^[0-9]{5,6}$")); |
| } |
| } |
| } |
| assertTrue(hasRegistered); |
| } |
| |
| /** |
| * Basic test to ensure {@link NetworkRegistrationInfo#isRoaming()} does not throw any |
| * exception. |
| */ |
| @Test |
| public void testNetworkRegistrationInfoIsRoaming() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // get NetworkRegistration object |
| NetworkRegistrationInfo nwReg = mTelephonyManager.getServiceState() |
| .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_CS, |
| AccessNetworkConstants.TRANSPORT_TYPE_WWAN); |
| assertThat(nwReg).isNotNull(); |
| nwReg.isRoaming(); |
| } |
| |
| /** |
| * Basic test to ensure {@link NetworkRegistrationInfo#getRoamingType()} does not throw any |
| * exception and returns valid result |
| * @see ServiceState.RoamingType |
| */ |
| @Test |
| public void testNetworkRegistrationInfoGetRoamingType() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // get NetworkRegistration object for voice |
| NetworkRegistrationInfo nwReg = mTelephonyManager.getServiceState() |
| .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_CS, |
| AccessNetworkConstants.TRANSPORT_TYPE_WWAN); |
| assertNotNull(nwReg); |
| assertThat(nwReg.getRoamingType()).isIn(ROAMING_TYPES); |
| |
| // getNetworkRegistration object for data |
| // get NetworkRegistration object for voice |
| nwReg = mTelephonyManager.getServiceState() |
| .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS, |
| AccessNetworkConstants.TRANSPORT_TYPE_WWAN); |
| assertThat(nwReg).isNotNull(); |
| assertThat(nwReg.getRoamingType()).isIn(ROAMING_TYPES); |
| } |
| |
| /** |
| * Basic test to ensure {@link NetworkRegistrationInfo#getAccessNetworkTechnology()} not |
| * throw any exception and returns valid result |
| * @see android.telephony.Annotation.NetworkType |
| */ |
| @Test |
| public void testNetworkRegistationStateGetAccessNetworkTechnology() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // get NetworkRegistration object for voice |
| NetworkRegistrationInfo nwReg = mTelephonyManager.getServiceState() |
| .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_CS, |
| AccessNetworkConstants.TRANSPORT_TYPE_WWAN); |
| assertThat(nwReg).isNotNull(); |
| assertThat(nwReg.getAccessNetworkTechnology()).isIn(NETWORK_TYPES); |
| |
| // get NetworkRegistation object for data |
| nwReg = mTelephonyManager.getServiceState() |
| .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS, |
| AccessNetworkConstants.TRANSPORT_TYPE_WWAN); |
| assertThat(nwReg).isNotNull(); |
| assertThat(nwReg.getAccessNetworkTechnology()).isIn(NETWORK_TYPES); |
| } |
| |
| |
| /** |
| * Tests that the device properly reports either a valid MEID or null. |
| */ |
| @Test |
| public void testGetMeid() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_CDMA)); |
| |
| String meid = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getMeid()); |
| |
| if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { |
| if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { |
| assertMeidEsn(meid); |
| } |
| } |
| } |
| |
| /** |
| * Tests that the device properly reports either a valid MEID or null. |
| */ |
| @Test |
| public void testGetMeidForSlot() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_CDMA)); |
| |
| SubscriptionManager sm = SubscriptionManager.from(getContext()); |
| List<SubscriptionInfo> subInfos = sm.getActiveSubscriptionInfoList(); |
| |
| if (subInfos != null) { |
| for (SubscriptionInfo subInfo : subInfos) { |
| int slotIndex = subInfo.getSimSlotIndex(); |
| int subId = subInfo.getSubscriptionId(); |
| TelephonyManager tm = mTelephonyManager.createForSubscriptionId(subId); |
| if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { |
| String meid = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, |
| (telephonyManager) -> telephonyManager.getMeid(slotIndex)); |
| |
| if (!TextUtils.isEmpty(meid)) { |
| assertMeidEsn(meid); |
| } |
| } |
| } |
| } |
| |
| // Also verify that no exception is thrown for any slot index (including invalid ones) |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getMeid(-1)); |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getMeid(mTelephonyManager.getPhoneCount())); |
| } |
| |
| /** |
| * Tests sendDialerSpecialCode API. |
| * Expects a security exception since the caller does not have carrier privileges or is not the |
| * current default dialer app. |
| */ |
| @Test |
| public void testSendDialerSpecialCode() { |
| try { |
| mTelephonyManager.sendDialerSpecialCode("4636"); |
| fail("Expected SecurityException. App does not have carrier privileges or is not the " |
| + "default dialer app"); |
| } catch (SecurityException expected) { |
| } |
| } |
| |
| /** |
| * Tests that the device properly reports the contents of EF_FPLMN or null |
| */ |
| @Test |
| public void testGetForbiddenPlmns() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| String[] plmns = mTelephonyManager.getForbiddenPlmns(); |
| |
| int phoneType = mTelephonyManager.getPhoneType(); |
| switch (phoneType) { |
| case TelephonyManager.PHONE_TYPE_GSM: |
| assertNotNull("Forbidden PLMNs must be valid or an empty list!", plmns); |
| case TelephonyManager.PHONE_TYPE_CDMA: |
| case TelephonyManager.PHONE_TYPE_NONE: |
| if (plmns == null) { |
| return; |
| } |
| } |
| |
| for(String plmn : plmns) { |
| assertTrue( |
| "Invalid Length for PLMN-ID, must be 5 or 6! plmn=" + plmn, |
| plmn.length() >= 5 && plmn.length() <= 6); |
| assertTrue( |
| "PLMNs must be strings of digits 0-9! plmn=" + plmn, |
| android.text.TextUtils.isDigitsOnly(plmn)); |
| } |
| } |
| |
| /** |
| * Tests that the device properly sets and pads the contents of EF_FPLMN |
| */ |
| @Test |
| public void testSetForbiddenPlmns() { |
| assumeTrue(supportSetFplmn()); |
| |
| String[] originalFplmns = mTelephonyManager.getForbiddenPlmns(); |
| try { |
| int numFplmnsSet = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.setForbiddenPlmns(FPLMN_TEST)); |
| String[] writtenFplmns = mTelephonyManager.getForbiddenPlmns(); |
| assertEquals("Wrong return value for setFplmns with less than required fplmns: " |
| + numFplmnsSet, FPLMN_TEST.size(), numFplmnsSet); |
| assertEquals("Wrong Fplmns content written", FPLMN_TEST, Arrays.asList(writtenFplmns)); |
| } finally { |
| // Restore |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.setForbiddenPlmns(Arrays.asList(originalFplmns))); |
| } |
| } |
| |
| /** |
| * Tests that the device properly truncates the contents of EF_FPLMN when provided size |
| * is too big. |
| */ |
| @Test |
| public void testSetForbiddenPlmnsTruncate() { |
| assumeTrue(supportSetFplmn()); |
| |
| String[] originalFplmns = mTelephonyManager.getForbiddenPlmns(); |
| try { |
| List<String> targetFplmns = new ArrayList<>(); |
| for (int i = 0; i < MIN_FPLMN_NUM; i++) { |
| targetFplmns.add(PLMN_A); |
| } |
| for (int i = MIN_FPLMN_NUM; i < MAX_FPLMN_NUM; i++) { |
| targetFplmns.add(PLMN_B); |
| } |
| int numFplmnsSet = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.setForbiddenPlmns(targetFplmns)); |
| String[] writtenFplmns = mTelephonyManager.getForbiddenPlmns(); |
| assertTrue("Wrong return value for setFplmns with overflowing fplmns: " + numFplmnsSet, |
| numFplmnsSet < MAX_FPLMN_NUM); |
| assertEquals("Number of Fplmns set does not equal number of Fplmns available", |
| numFplmnsSet, writtenFplmns.length); |
| assertEquals("Wrong Fplmns content written", targetFplmns.subList(0, numFplmnsSet), |
| Arrays.asList(writtenFplmns)); |
| } finally { |
| // Restore |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.setForbiddenPlmns(Arrays.asList(originalFplmns))); |
| } |
| } |
| |
| /** |
| * Tests that the device properly deletes the contents of EF_FPLMN |
| */ |
| @Test |
| public void testSetForbiddenPlmnsDelete() { |
| assumeTrue(supportSetFplmn()); |
| |
| String[] originalFplmns = mTelephonyManager.getForbiddenPlmns(); |
| try { |
| // Support test for empty SIM |
| List<String> targetDummyFplmns = new ArrayList<>(); |
| for (int i = 0; i < MIN_FPLMN_NUM; i++) { |
| targetDummyFplmns.add(PLMN_A); |
| } |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.setForbiddenPlmns(targetDummyFplmns)); |
| String[] writtenDummyFplmns = mTelephonyManager.getForbiddenPlmns(); |
| assertEquals(targetDummyFplmns, Arrays.asList(writtenDummyFplmns)); |
| |
| List<String> targetFplmns = new ArrayList<>(); |
| int numFplmnsSet = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.setForbiddenPlmns(targetFplmns)); |
| String[] writtenFplmns = mTelephonyManager.getForbiddenPlmns(); |
| assertEquals("Wrong return value for setFplmns with empty list", 0, numFplmnsSet); |
| assertEquals("Wrong number of Fplmns written", 0, writtenFplmns.length); |
| // TODO wait for 10 minutes or so for the FPLMNS list to grow back |
| } finally { |
| // Restore |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.setForbiddenPlmns(Arrays.asList(originalFplmns))); |
| } |
| } |
| |
| |
| /** |
| * Tests that setForbiddenPlmns properly handles null input |
| */ |
| @Test |
| public void testSetForbiddenPlmnsVoid() { |
| assumeTrue(supportSetFplmn()); |
| |
| String[] originalFplmns = mTelephonyManager.getForbiddenPlmns(); |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.setForbiddenPlmns(null)); |
| fail("Expected IllegalArgumentException. Null input is not allowed"); |
| } catch (IllegalArgumentException expected) { |
| } finally { |
| // Restore |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.setForbiddenPlmns(Arrays.asList(originalFplmns))); |
| } |
| } |
| |
| @Test |
| public void testGetEquivalentHomePlmns() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| List<String> plmns = mTelephonyManager.getEquivalentHomePlmns(); |
| |
| if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { |
| assertEquals(0, plmns.size()); |
| } else { |
| for (String plmn : plmns) { |
| assertTrue( |
| "Invalid Length for PLMN-ID, must be 5 or 6! plmn=" + plmn, |
| plmn.length() >= 5 && plmn.length() <= 6); |
| assertTrue( |
| "PLMNs must be strings of digits 0-9! plmn=" + plmn, |
| android.text.TextUtils.isDigitsOnly(plmn)); |
| } |
| } |
| } |
| |
| /** |
| * Tests that the device properly reports the contents of ManualNetworkSelectionPlmn |
| * The setting is not persisted selection |
| */ |
| @Test |
| public void testGetManualNetworkSelectionPlmnNonPersisted() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| if (mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_GSM) return; |
| |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.setNetworkSelectionModeManual( |
| TESTING_PLMN/* operatorNumeric */, false /* persistSelection */)); |
| String plmn = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getManualNetworkSelectionPlmn()); |
| assertEquals(TESTING_PLMN, plmn); |
| } finally { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setNetworkSelectionModeAutomatic()); |
| } |
| } |
| |
| /** |
| * Tests that the device properly reports the contents of ManualNetworkSelectionPlmn |
| * The setting is persisted selection |
| */ |
| @Test |
| public void testGetManualNetworkSelectionPlmnPersisted() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| if (mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_GSM) return; |
| |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.setNetworkSelectionModeManual( |
| TESTING_PLMN/* operatorNumeric */, true /* persistSelection */)); |
| String plmn = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getManualNetworkSelectionPlmn()); |
| assertEquals(TESTING_PLMN, plmn); |
| } finally { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setNetworkSelectionModeAutomatic()); |
| } |
| } |
| |
| /** |
| * Verify that TelephonyManager.getCardIdForDefaultEuicc returns a positive value or either |
| * UNINITIALIZED_CARD_ID or UNSUPPORTED_CARD_ID. |
| */ |
| @Test |
| public void testGetCardIdForDefaultEuicc() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_EUICC)); |
| |
| int cardId = mTelephonyManager.getCardIdForDefaultEuicc(); |
| assertTrue("Card ID for default EUICC is not a valid value", |
| cardId == TelephonyManager.UNSUPPORTED_CARD_ID |
| || cardId == TelephonyManager.UNINITIALIZED_CARD_ID |
| || cardId >= 0); |
| } |
| |
| /** |
| * Tests that a SecurityException is thrown when trying to access UiccCardsInfo. |
| */ |
| @Test |
| public void testGetUiccCardsInfoException() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| try { |
| // Requires READ_PRIVILEGED_PHONE_STATE or carrier privileges |
| List<UiccCardInfo> infos = mTelephonyManager.getUiccCardsInfo(); |
| fail("Expected SecurityException. App does not have carrier privileges"); |
| } catch (SecurityException e) { |
| } |
| } |
| |
| /** |
| * Tests that UiccCardsInfo methods don't crash. |
| */ |
| @Test |
| public void testGetUiccCardsInfo() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| // The API requires either READ_PRIVILEGED_PHONE_STATE or carrier privileges |
| try { |
| mTelephonyManager.getUiccCardsInfo(); |
| fail("Telephony#getUiccCardsInfo should throw SecurityException without " |
| + "READ_PRIVILEGED_PHONE_STATE nor carrier privileges"); |
| } catch (SecurityException expected) { |
| } |
| |
| // With READ_PRIVILEGED_PHONE_STATE only, it should work |
| List<UiccCardInfo> infos = |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getUiccCardsInfo()); |
| // test that these methods don't crash |
| if (infos.size() > 0) { |
| UiccCardInfo info = infos.get(0); |
| info.getEid(); |
| info.isRemovable(); |
| info.isEuicc(); |
| info.getCardId(); |
| info.getPorts(); |
| info.getPhysicalSlotIndex(); |
| info.isRemovable(); |
| } |
| |
| // With carrier privileges only, it should also work |
| try { |
| CarrierPrivilegeUtils.withCarrierPrivileges( |
| getContext(), |
| SubscriptionManager.getDefaultSubscriptionId(), |
| () -> mTelephonyManager.getUiccCardsInfo()); |
| } catch (SecurityException se) { |
| fail("TelephonyManager.getUiccCardsInfo should not throw SecurityException with " |
| + "carrier privileges"); |
| } catch (Exception e) { |
| fail("Exception thrown when try to get carrier privileges."); |
| } |
| } |
| |
| private static Context getContext() { |
| return InstrumentationRegistry.getContext(); |
| } |
| |
| /** |
| * Tests that the device properly reports the contents of NetworkSelectionMode |
| */ |
| @Test |
| public void testGetNetworkSelectionMode() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setNetworkSelectionModeAutomatic()); |
| } catch (Exception e) { |
| } |
| |
| int networkMode = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getNetworkSelectionMode()); |
| |
| assertEquals(TelephonyManager.NETWORK_SELECTION_MODE_AUTO, networkMode); |
| } |
| |
| /** |
| * Tests that the device properly sets the network selection mode to automatic. |
| * Expects a security exception since the caller does not have carrier privileges. |
| */ |
| @Test |
| public void testSetNetworkSelectionModeAutomatic() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| try { |
| mTelephonyManager.setNetworkSelectionModeAutomatic(); |
| fail("Expected SecurityException. App does not have carrier privileges."); |
| } catch (SecurityException expected) { |
| } |
| } |
| |
| /** |
| * Tests that the device properly asks the radio to connect to the input network and change |
| * selection mode to manual. |
| * Expects a security exception since the caller does not have carrier privileges. |
| */ |
| @Test |
| public void testSetNetworkSelectionModeManual() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| try { |
| mTelephonyManager.setNetworkSelectionModeManual( |
| "" /* operatorNumeric */, false /* persistSelection */); |
| fail("Expected SecurityException. App does not have carrier privileges."); |
| } catch (SecurityException expected) { |
| } |
| } |
| |
| /** |
| * Tests that the device properly check whether selection mode was manual. |
| */ |
| @Test |
| public void testIsManualNetworkSelectionAllowed() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| if (mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_GSM) return; |
| |
| assertTrue(ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.isManualNetworkSelectionAllowed())); |
| } |
| |
| /** |
| * Tests that the device properly sets the VoNr |
| */ |
| @Test |
| public void testIsVoNrEnabled() { |
| if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { |
| return; |
| } |
| |
| try { |
| int result = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.setVoNrEnabled(true)); |
| if (result == TelephonyManager.ENABLE_VONR_REQUEST_NOT_SUPPORTED) { |
| return; |
| } |
| } catch (Exception e) { |
| } |
| |
| assertTrue(ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.isVoNrEnabled())); |
| } |
| |
| /** |
| * Tests that a SecurityException is thrown when trying to set VoNR |
| */ |
| @Test |
| public void testSetVoNrEnabledException() { |
| if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { |
| Log.d(TAG, "Skipping test that requires FEATURE_TELEPHONY"); |
| return; |
| } |
| try { |
| mTelephonyManager.setVoNrEnabled(true); |
| fail("Expected SecurityException. App does not have carrier privileges."); |
| } catch (SecurityException expected) { |
| } |
| } |
| |
| /** |
| * Construct a CallAttributes object and test getters. |
| */ |
| @Test |
| public void testCallAttributes() { |
| CallQuality cq = new CallQuality(); |
| PreciseCallState pcs = new PreciseCallState(); |
| CallAttributes ca = new CallAttributes(pcs, TelephonyManager.NETWORK_TYPE_UNKNOWN, cq); |
| assertEquals(pcs, ca.getPreciseCallState()); |
| assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, ca.getNetworkType()); |
| assertEquals(cq, ca.getCallQuality()); |
| } |
| |
| /** |
| * Checks that a zeroed-out default CallQuality object can be created |
| */ |
| @Test |
| public void testCallQuality() { |
| CallQuality cq = new CallQuality(); |
| assertEquals(0, cq.getDownlinkCallQualityLevel()); |
| assertEquals(0, cq.getUplinkCallQualityLevel()); |
| assertEquals(0, cq.getCallDuration()); |
| assertEquals(0, cq.getNumRtpPacketsTransmitted()); |
| assertEquals(0, cq.getNumRtpPacketsReceived()); |
| assertEquals(0, cq.getNumRtpPacketsTransmittedLost()); |
| assertEquals(0, cq.getNumRtpPacketsNotReceived()); |
| assertEquals(0, cq.getAverageRelativeJitter()); |
| assertEquals(0, cq.getMaxRelativeJitter()); |
| assertEquals(0, cq.getAverageRoundTripTime()); |
| assertEquals(0, cq.getCodecType()); |
| assertEquals(false, cq.isRtpInactivityDetected()); |
| assertEquals(false, cq.isIncomingSilenceDetectedAtCallSetup()); |
| assertEquals(false, cq.isOutgoingSilenceDetectedAtCallSetup()); |
| assertEquals(0, cq.getNumVoiceFrames()); |
| assertEquals(0, cq.getNumNoDataFrames()); |
| assertEquals(0, cq.getNumDroppedRtpPackets()); |
| assertEquals(0, cq.getMinPlayoutDelayMillis()); |
| assertEquals(0, cq.getMaxPlayoutDelayMillis()); |
| assertEquals(0, cq.getNumRtpSidPacketsReceived()); |
| assertEquals(0, cq.getNumRtpDuplicatePackets()); |
| } |
| |
| /** |
| * Validate CallQuality Parcel |
| */ |
| @Test |
| public void testCallQualityParcel() { |
| CallQuality cq = new CallQuality.Builder() |
| .setDownlinkCallQualityLevel(CallQuality.CALL_QUALITY_NOT_AVAILABLE) |
| .setUplinkCallQualityLevel(CallQuality.CALL_QUALITY_NOT_AVAILABLE) |
| .setCallDurationMillis(20000) |
| .setNumRtpPacketsTransmitted(550) |
| .setNumRtpPacketsReceived(450) |
| .setNumRtpPacketsTransmittedLost(4) |
| .setNumRtpPacketsNotReceived(6) |
| .setAverageRelativeJitter(20) |
| .setMaxRelativeJitter(30) |
| .setAverageRoundTripTimeMillis(150) |
| .setCodecType(0) |
| .setRtpInactivityDetected(false) |
| .setIncomingSilenceDetectedAtCallSetup(false) |
| .setOutgoingSilenceDetectedAtCallSetup(false) |
| .setNumVoiceFrames(300) |
| .setNumNoDataFrames(300) |
| .setNumDroppedRtpPackets(5) |
| .setMinPlayoutDelayMillis(500) |
| .setMaxPlayoutDelayMillis(1000) |
| .setNumRtpSidPacketsReceived(300) |
| .setNumRtpDuplicatePackets(0) |
| .build(); |
| |
| Parcel stateParcel = Parcel.obtain(); |
| cq.writeToParcel(stateParcel, 0); |
| stateParcel.setDataPosition(0); |
| |
| CallQuality parcelCq = CallQuality.CREATOR.createFromParcel(stateParcel); |
| assertThat(cq).isEqualTo(parcelCq); |
| |
| } |
| |
| // Reference: packages/services/Telephony/ecc/input/eccdata.txt |
| private static final Map<String, String> EMERGENCY_NUMBERS_FOR_COUNTRIES = Map.of( |
| "au", "000", |
| "ca", "911", |
| "de", "112", |
| "gb", "999", |
| "in", "112", |
| "jp", "110", |
| "sg", "999", |
| "tw", "110", |
| "us", "911"); |
| |
| /** |
| * Tests TelephonyManager.getEmergencyNumberList. |
| * |
| * Also enforce country-specific emergency number in CTS. |
| */ |
| @Test |
| public void testGetEmergencyNumberList() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_CALLING)); |
| |
| Map<Integer, List<EmergencyNumber>> emergencyNumberList = |
| mTelephonyManager.getEmergencyNumberList(); |
| |
| assertFalse(emergencyNumberList == null); |
| |
| checkEmergencyNumberFormat(emergencyNumberList); |
| |
| int defaultSubId = mSubscriptionManager.getDefaultSubscriptionId(); |
| for (Map.Entry<String, String> entry : EMERGENCY_NUMBERS_FOR_COUNTRIES.entrySet()) { |
| if (mTelephonyManager.getNetworkCountryIso().equals(entry.getKey())) { |
| assertTrue(checkIfEmergencyNumberListHasSpecificAddress( |
| emergencyNumberList.get(defaultSubId), entry.getValue())); |
| } |
| } |
| } |
| |
| /** |
| * Tests TelephonyManager.getEmergencyNumberList(@EmergencyServiceCategories int categories). |
| * |
| */ |
| @Test |
| public void testGetEmergencyNumberListForCategories() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_CALLING)); |
| |
| Map<Integer, List<EmergencyNumber>> emergencyNumberList = |
| mTelephonyManager.getEmergencyNumberList( |
| EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE); |
| |
| assertFalse(emergencyNumberList == null); |
| |
| checkEmergencyNumberFormat(emergencyNumberList); |
| |
| int defaultSubId = mSubscriptionManager.getDefaultSubscriptionId(); |
| final String country_us = "us"; |
| final String country_us_police_number = "911"; |
| if (mTelephonyManager.getNetworkCountryIso().equals(country_us)) { |
| assertTrue(checkIfEmergencyNumberListHasSpecificAddress( |
| emergencyNumberList.get(defaultSubId), country_us_police_number)); |
| } |
| for (EmergencyNumber num : emergencyNumberList.get(defaultSubId)) { |
| assertTrue(num.isInEmergencyServiceCategories( |
| EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE)); |
| } |
| } |
| |
| /** |
| * Tests TelephonyManager.isEmergencyNumber. |
| * |
| * Also enforce country-specific emergency number in CTS. |
| */ |
| @Test |
| public void testIsEmergencyNumber() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_CALLING)); |
| |
| for (Map.Entry<String, String> entry : EMERGENCY_NUMBERS_FOR_COUNTRIES.entrySet()) { |
| if (mTelephonyManager.getNetworkCountryIso().equals(entry.getKey())) { |
| assertTrue(mTelephonyManager.isEmergencyNumber(entry.getValue())); |
| } |
| } |
| } |
| |
| /** |
| * Tests TelephonyManager.isPotentialEmergencyNumber. |
| */ |
| @Test |
| public void testIsPotentialEmergencyNumber() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_CALLING)); |
| //NOTE: TelephonyManager#isPotentialEmergencyNumber is a hidden |
| //and now deprecated API (from Android-U). This test is updated to make sure we never |
| //do a "potential" match, but always use "exact" matching since it can cause issues |
| //in countries where regular numbers can end up being treated as emergency numbers. |
| String countryIso = mTelephonyManager.getNetworkCountryIso(); |
| String potentialEmergencyAddress = "91112345"; |
| assertFalse(ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.isPotentialEmergencyNumber(potentialEmergencyAddress))); |
| } |
| |
| /** |
| * Tests TelephonyManager.setCallComposerStatus and TelephonyManager.getCallComposerStatus. |
| */ |
| @Test |
| public void testSetGetCallComposerStatus() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_CALLING)); |
| |
| if (hasFeature(PackageManager.FEATURE_TELEPHONY_IMS)) { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setCallComposerStatus(TelephonyManager.CALL_COMPOSER_STATUS_OFF)); |
| int status = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| tm -> tm.getCallComposerStatus()); |
| assertThat(status).isEqualTo(TelephonyManager.CALL_COMPOSER_STATUS_OFF); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setCallComposerStatus(TelephonyManager.CALL_COMPOSER_STATUS_ON)); |
| status = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| tm -> tm.getCallComposerStatus()); |
| assertThat(status).isEqualTo(TelephonyManager.CALL_COMPOSER_STATUS_ON); |
| } else { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setCallComposerStatus(TelephonyManager.CALL_COMPOSER_STATUS_OFF)); |
| int status = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| tm -> tm.getCallComposerStatus()); |
| assertThat(status).isEqualTo(TelephonyManager.CALL_COMPOSER_STATUS_OFF); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setCallComposerStatus(TelephonyManager.CALL_COMPOSER_STATUS_ON)); |
| status = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| tm -> tm.getCallComposerStatus()); |
| assertThat(status).isEqualTo(TelephonyManager.CALL_COMPOSER_STATUS_OFF); |
| } |
| } |
| |
| /** |
| * Tests {@link TelephonyManager#getSupportedRadioAccessFamily()} |
| */ |
| @Test |
| public void testGetRadioAccessFamily() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| long raf = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getSupportedRadioAccessFamily()); |
| assertThat(raf).isNotEqualTo(TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN); |
| } |
| |
| private static void assertSetOpportunisticSubSuccess(int value) { |
| assertThat(value).isEqualTo(TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS); |
| } |
| |
| private static void assertSetOpportunisticNoOpportunisticSub(int value) { |
| assertThat(value).isEqualTo( |
| TelephonyManager.SET_OPPORTUNISTIC_SUB_NO_OPPORTUNISTIC_SUB_AVAILABLE); |
| } |
| |
| /** |
| * Tests {@link TelephonyManager#setPreferredOpportunisticDataSubscription} and |
| * {@link TelephonyManager#getPreferredOpportunisticDataSubscription} |
| */ |
| @Test |
| public void testPreferredOpportunisticDataSubscription() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_DATA)); |
| |
| int randomSubId = 1; |
| int activeSubscriptionInfoCount = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mSubscriptionManager, (tm) -> tm.getActiveSubscriptionInfoCount()); |
| if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { |
| return; |
| } |
| if (mTelephonyManager.getPhoneCount() == 1) { |
| return; |
| } |
| if (mTelephonyManager.getPhoneCount() == 2 && activeSubscriptionInfoCount != 2) { |
| return; |
| } |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setPreferredOpportunisticDataSubscription( |
| SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, |
| null, null)); |
| // wait for the data change to take effect |
| waitForMs(500); |
| int subId = |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getPreferredOpportunisticDataSubscription()); |
| assertThat(subId).isEqualTo(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); |
| List<SubscriptionInfo> subscriptionInfoList = |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mSubscriptionManager, |
| (tm) -> tm.getOpportunisticSubscriptions()); |
| Consumer<Integer> callbackSuccess = TelephonyManagerTest::assertSetOpportunisticSubSuccess; |
| Consumer<Integer> callbackNoOpSub = |
| TelephonyManagerTest::assertSetOpportunisticNoOpportunisticSub; |
| if (subscriptionInfoList == null || subscriptionInfoList.size() == 0) { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setPreferredOpportunisticDataSubscription(randomSubId, false, |
| AsyncTask.SERIAL_EXECUTOR, callbackNoOpSub)); |
| // wait for the data change to take effect |
| waitForMs(500); |
| subId = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getPreferredOpportunisticDataSubscription()); |
| assertThat(subId).isEqualTo(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); |
| } else { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setPreferredOpportunisticDataSubscription( |
| subscriptionInfoList.get(0).getSubscriptionId(), false, |
| AsyncTask.SERIAL_EXECUTOR, callbackSuccess)); |
| // wait for the data change to take effect |
| waitForMs(500); |
| subId = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getPreferredOpportunisticDataSubscription()); |
| assertThat(subId).isEqualTo(subscriptionInfoList.get(0).getSubscriptionId()); |
| } |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setPreferredOpportunisticDataSubscription( |
| SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, |
| AsyncTask.SERIAL_EXECUTOR, callbackSuccess)); |
| // wait for the data change to take effect |
| waitForMs(500); |
| subId = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getPreferredOpportunisticDataSubscription()); |
| assertThat(subId).isEqualTo(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); |
| } |
| |
| private static void assertUpdateAvailableNetworkSuccess(int value) { |
| assertThat(value).isEqualTo(TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS); |
| } |
| |
| private static void assertUpdateAvailableNetworkNoOpportunisticSub(int value) { |
| assertThat(value).isEqualTo( |
| TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE); |
| } |
| |
| private static boolean checkIfEmergencyNumberListHasSpecificAddress( |
| List<EmergencyNumber> emergencyNumberList, String address) { |
| for (EmergencyNumber emergencyNumber : emergencyNumberList) { |
| if (address.equals(emergencyNumber.getNumber())) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private static void checkEmergencyNumberFormat( |
| Map<Integer, List<EmergencyNumber>> emergencyNumberLists) { |
| for (List<EmergencyNumber> emergencyNumberList : emergencyNumberLists.values()) { |
| for (EmergencyNumber emergencyNumber : emergencyNumberList) { |
| |
| // Validate Emergency number address |
| assertTrue(validateEmergencyNumberAddress(emergencyNumber.getNumber())); |
| |
| // Validate Emergency number country Iso |
| assertTrue(validateEmergencyNumberCountryIso(emergencyNumber.getCountryIso())); |
| |
| // Validate Emergency number mnc |
| assertTrue(validateEmergencyNumberMnc(emergencyNumber.getMnc())); |
| |
| // Validate Emergency service category list |
| assertTrue(validateEmergencyServiceCategoryList( |
| emergencyNumber.getEmergencyServiceCategories())); |
| |
| // Validate Emergency number source list |
| assertTrue(validateEmergencyNumberSourceList( |
| emergencyNumber.getEmergencyNumberSources())); |
| |
| // Validate Emergency URN list |
| // (just verify it is not null, because the support of this field is optional) |
| assertTrue(emergencyNumber.getEmergencyUrns() != null); |
| |
| // Validat Emergency call routing |
| assertTrue(validateEmergencyCallRouting( |
| emergencyNumber.getEmergencyCallRouting())); |
| |
| // Valid the emergency number should be at least in a valid source. |
| assertTrue(validateEmergencyNumberFromAnySource(emergencyNumber)); |
| |
| // Valid the emergency number should be at least in a valid category. |
| assertTrue(validateEmergencyNumberInAnyCategory(emergencyNumber)); |
| } |
| |
| // Validate compareTo |
| assertTrue(validateEmergencyNumberCompareTo(emergencyNumberList)); |
| } |
| } |
| |
| /** |
| * Tests {@link TelephonyManager#updateAvailableNetworks} |
| */ |
| @Test |
| public void testUpdateAvailableNetworks() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| int randomSubId = 1; |
| int activeSubscriptionInfoCount = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mSubscriptionManager, (tm) -> tm.getActiveSubscriptionInfoCount()); |
| boolean isOpportunisticNetworkEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isOpportunisticNetworkEnabled()); |
| |
| if (!isOpportunisticNetworkEnabled) { |
| return; |
| } |
| if (mTelephonyManager.getPhoneCount() == 1) { |
| return; |
| } |
| if (mTelephonyManager.getPhoneCount() == 2 && activeSubscriptionInfoCount != 2) { |
| return; |
| } |
| |
| List<SubscriptionInfo> subscriptionInfoList = |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mSubscriptionManager, |
| (tm) -> tm.getOpportunisticSubscriptions()); |
| List<String> mccMncs = new ArrayList<String>(); |
| List<Integer> bands = new ArrayList<Integer>(); |
| List<AvailableNetworkInfo> availableNetworkInfos = new ArrayList<AvailableNetworkInfo>(); |
| Consumer<Integer> callbackSuccess = |
| TelephonyManagerTest::assertUpdateAvailableNetworkSuccess; |
| Consumer<Integer> callbackNoOpSub = |
| TelephonyManagerTest::assertUpdateAvailableNetworkNoOpportunisticSub; |
| if (subscriptionInfoList == null || subscriptionInfoList.size() == 0 |
| || !mSubscriptionManager.isActiveSubscriptionId( |
| subscriptionInfoList.get(0).getSubscriptionId())) { |
| AvailableNetworkInfo availableNetworkInfo = new AvailableNetworkInfo(randomSubId, |
| AvailableNetworkInfo.PRIORITY_HIGH, mccMncs, bands); |
| availableNetworkInfos.add(availableNetworkInfo); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.updateAvailableNetworks(availableNetworkInfos, |
| AsyncTask.SERIAL_EXECUTOR, callbackNoOpSub)); |
| // wait for the data change to take effect |
| waitForMs(500); |
| // clear all the operations at the end of test. |
| availableNetworkInfos.clear(); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.updateAvailableNetworks(availableNetworkInfos, |
| AsyncTask.SERIAL_EXECUTOR, callbackNoOpSub)); |
| } else { |
| AvailableNetworkInfo availableNetworkInfo = new AvailableNetworkInfo( |
| subscriptionInfoList.get(0).getSubscriptionId(), |
| AvailableNetworkInfo.PRIORITY_HIGH, mccMncs, bands); |
| availableNetworkInfos.add(availableNetworkInfo); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.updateAvailableNetworks(availableNetworkInfos, |
| AsyncTask.SERIAL_EXECUTOR, callbackSuccess)); |
| // wait for the data change to take effect |
| waitForMs(500); |
| // clear all the operations at the end of test. |
| availableNetworkInfos.clear(); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.updateAvailableNetworks(availableNetworkInfos, |
| AsyncTask.SERIAL_EXECUTOR, callbackSuccess)); |
| } |
| } |
| |
| @Test |
| public void testSwitchMultiSimConfig() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| try { |
| mTelephonyManager.switchMultiSimConfig(mTelephonyManager.getActiveModemCount()); |
| fail("TelephonyManager#switchMultiSimConfig should require the MODIFY_PHONE_STATE" |
| + " permission to access."); |
| } catch (SecurityException e) { |
| // expected |
| } |
| try { |
| // This should result in no-op. |
| ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.switchMultiSimConfig(mTelephonyManager.getActiveModemCount()), |
| SecurityException.class, android.Manifest.permission.MODIFY_PHONE_STATE); |
| } catch (SecurityException e) { |
| fail("TelephonyManager#switchMultiSimConfig should require MODIFY_PHONE_STATE" |
| + "permission to access."); |
| } |
| } |
| |
| @Test |
| public void testIccOpenLogicalChannelBySlot() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| // just verify no crash |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.iccOpenLogicalChannelBySlot(0, null, 0)); |
| } catch (IllegalArgumentException e) { |
| // IllegalArgumentException is okay, just not SecurityException |
| } |
| } |
| |
| @Test |
| public void testIccOpenLogicalChannelBySlotAndPort() { |
| if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { |
| return; |
| } |
| // just verify no crash |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.iccOpenLogicalChannelByPort(0, 0, null, 0)); |
| } catch (SecurityException e) { |
| // IllegalArgumentException is okay, just not SecurityException |
| fail("iccCloseLogicalChannelByPort: SecurityException not expected"); |
| } |
| } |
| |
| @Test |
| public void testIccCloseLogicalChannelBySlot() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| // just verify no crash |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.iccCloseLogicalChannelBySlot(0, 0)); |
| } catch (IllegalArgumentException e) { |
| // IllegalArgumentException is okay, just not SecurityException |
| } |
| } |
| @Test |
| public void testIccCloseLogicalChannelBySlotAndPort() { |
| if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { |
| return; |
| } |
| int slotIndex = getValidSlotIndexAndPort().getKey(); |
| int portIndex = getValidSlotIndexAndPort().getValue(); |
| // just verify no crash |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, (tm) -> tm.iccCloseLogicalChannelByPort( |
| slotIndex, portIndex, 0)); |
| } catch (IllegalArgumentException | IllegalStateException e) { |
| // IllegalArgumentException and IllegalStateException is okay, just not |
| // SecurityException |
| } catch (SecurityException e) { |
| // IllegalArgumentException is okay, just not SecurityException |
| fail("iccCloseLogicalChannelByPort: SecurityException not expected"); |
| } |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, (tm) -> tm.iccCloseLogicalChannelByPort(slotIndex, -1, 0)); |
| fail("Expected IllegalArgumentException, invalid PortIndex"); |
| } catch (IllegalArgumentException e) { |
| // IllegalArgumentException is expected |
| } |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, (tm) -> tm.iccCloseLogicalChannelByPort( |
| slotIndex, portIndex, -1)); |
| fail("Expected IllegalArgumentException, invalid channel"); |
| } catch (IllegalArgumentException e) { |
| // IllegalArgumentException is expected |
| } |
| } |
| |
| @Test |
| public void testIccTransmitApduLogicalChannelBySlot() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| int slotIndex = getValidSlotIndexAndPort().getKey(); |
| String result = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.iccTransmitApduLogicalChannelBySlot( |
| slotIndex, |
| 0 /* channel */, |
| 0 /* cla */, |
| 0 /* instruction */, |
| 0 /* p1 */, |
| 0 /* p2 */, |
| 0 /* p3 */, |
| null /* data */)); |
| assertTrue(TextUtils.isEmpty(result)); |
| } |
| |
| @Test |
| public void testIccTransmitApduLogicalChannelBySlotAndPort() { |
| if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { |
| return; |
| } |
| int slotIndex = getValidSlotIndexAndPort().getKey(); |
| int portIndex = getValidSlotIndexAndPort().getValue(); |
| try { |
| String result = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.iccTransmitApduLogicalChannelByPort( |
| slotIndex, |
| portIndex /* portIndex */, |
| 0 /* channel */, |
| 0 /* cla */, |
| 0 /* instruction */, |
| 0 /* p1 */, |
| 0 /* p2 */, |
| 0 /* p3 */, |
| null /* data */)); |
| assertTrue(TextUtils.isEmpty(result)); |
| } catch (SecurityException e) { |
| // IllegalArgumentException is okay, just not SecurityException |
| fail("iccTransmitApduLogicalChannelByPort: SecurityException not expected"); |
| } |
| } |
| @Test |
| public void testIccTransmitApduBasicChannelBySlot() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| // just verify no crash |
| int slotIndex = getValidSlotIndexAndPort().getKey(); |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.iccTransmitApduBasicChannelBySlot( |
| slotIndex, |
| 0 /* cla */, |
| 0 /* instruction */, |
| 0 /* p1 */, |
| 0 /* p2 */, |
| 0 /* p3 */, |
| null /* data */)); |
| } catch (IllegalArgumentException e ) { |
| // IllegalArgumentException is okay, just not SecurityException |
| } |
| } |
| |
| @Test |
| public void testIccTransmitApduBasicChannelBySlotAndPort() { |
| if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { |
| return; |
| } |
| // just verify no crash |
| int slotIndex = getValidSlotIndexAndPort().getKey(); |
| int portIndex = getValidSlotIndexAndPort().getValue(); |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.iccTransmitApduBasicChannelByPort( |
| slotIndex, |
| portIndex /*portIndex */, |
| 0 /* cla */, |
| 0 /* instruction */, |
| 0 /* p1 */, |
| 0 /* p2 */, |
| 0 /* p3 */, |
| null /* data */)); |
| } catch (SecurityException e) { |
| // IllegalArgumentException is okay, just not SecurityException |
| fail("iccTransmitApduBasicChannelByPort: SecurityException not expected"); |
| } |
| } |
| |
| @Test |
| public void testIsIccLockEnabled() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| // verify SecurityException |
| try { |
| mTelephonyManager.isIccLockEnabled(); |
| fail("testIsIccLockEnabled: Expected SecurityException on isIccLockEnabled"); |
| } catch (SecurityException se) { |
| // expected |
| } |
| |
| // test with permission |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isIccLockEnabled()); |
| } catch (SecurityException se) { |
| fail("testIsIccLockEnabled: SecurityException not expected"); |
| } |
| } |
| |
| @Test |
| public void testIsDataEnabledForApn() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_DATA)); |
| |
| // verify SecurityException |
| try { |
| mTelephonyManager.isDataEnabledForApn(ApnSetting.TYPE_MMS); |
| fail("testIsDataEnabledForApn: Expected SecurityException on isDataEnabledForApn"); |
| } catch (SecurityException se) { |
| // expected |
| } |
| |
| // test with permission |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isDataEnabledForApn(ApnSetting.TYPE_MMS)); |
| } catch (SecurityException se) { |
| fail("testIsDataEnabledForApn: SecurityException not expected"); |
| } |
| } |
| |
| @Test |
| public void testIsTetheringApnRequired() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_DATA)); |
| |
| // verify SecurityException |
| try { |
| mTelephonyManager.isTetheringApnRequired(); |
| fail("testIsTetheringApnRequired: Expected SecurityException on " |
| + "isTetheringApnRequired"); |
| } catch (SecurityException se) { |
| // expected |
| } |
| |
| // test with permission |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isTetheringApnRequired()); |
| } catch (SecurityException se) { |
| fail("testIsIccLockEnabled: SecurityException not expected"); |
| } |
| } |
| |
| @Test |
| public void testGetCarrierInfoForImsiEncryption() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| // test without permission: verify SecurityException |
| try { |
| mTelephonyManager.getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_EPDG); |
| fail("testGetCarrierInfoForImsiEncryption: " |
| + "SecurityException expected on getCarrierInfoForImsiEncryption"); |
| } catch (SecurityException se) { |
| // expected |
| } |
| try { |
| mTelephonyManager.getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_WLAN); |
| fail("testGetCarrierInfoForImsiEncryption: " |
| + "SecurityException expected on getCarrierInfoForImsiEncryption"); |
| } catch (SecurityException se) { |
| // expected |
| } |
| // test with permission |
| PublicKey epdgKey = null; |
| PublicKey wlanKey = null; |
| try { |
| PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mTestSub); |
| |
| assertNotNull("CarrierConfigManager#getConfigForSubId() returned null", |
| carrierConfig); |
| assertFalse("CarrierConfigManager#getConfigForSubId() returned empty bundle", |
| carrierConfig.isEmpty()); |
| |
| // purge the certs in carrierConfigs first |
| carrierConfig.putInt( |
| CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT, 3); |
| carrierConfig.putString( |
| CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING, BAD_IMSI_CERT_URL); |
| carrierConfig.putString( |
| CarrierConfigManager.IMSI_CARRIER_PUBLIC_KEY_EPDG_STRING, |
| IMSI_CERT_STRING_EPDG); |
| carrierConfig.putString( |
| CarrierConfigManager.IMSI_CARRIER_PUBLIC_KEY_WLAN_STRING, |
| IMSI_CERT_STRING_WLAN); |
| overrideCarrierConfig(carrierConfig); |
| } catch (Exception e) { |
| fail("Could not override carrier config. e=" + e.toString()); |
| } |
| |
| try { |
| // It appears that the two certs actually have the same public key. Ideally we would |
| // want these to be different for testing, but it's challenging to create a valid |
| // certificate string for testing and these are the only two examples available |
| InputStream inStream = new ByteArrayInputStream(IMSI_CERT_STRING_WLAN.getBytes()); |
| CertificateFactory cf = CertificateFactory.getInstance("X.509"); |
| X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream); |
| wlanKey = cert.getPublicKey(); |
| |
| inStream = new ByteArrayInputStream(IMSI_CERT_STRING_EPDG.getBytes()); |
| cert = (X509Certificate) cf.generateCertificate(inStream); |
| epdgKey = cert.getPublicKey(); |
| } catch (CertificateException e) { |
| fail("Could not create certs. e=" + e.toString()); |
| } |
| |
| try { |
| ImsiEncryptionInfo info = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, |
| (tm) -> { |
| return tm.getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_EPDG); |
| }); |
| assertNotNull("Encryption info returned null", info); |
| assertEquals(epdgKey, info.getPublicKey()); |
| assertEquals(TelephonyManager.KEY_TYPE_EPDG, info.getKeyType()); |
| } catch (SecurityException se) { |
| fail("testGetCarrierInfoForImsiEncryption: SecurityException not expected"); |
| } catch (IllegalArgumentException iae) { |
| // IllegalArgumentException is okay, just not SecurityException |
| } |
| try { |
| ImsiEncryptionInfo info = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, |
| (tm) -> { |
| return tm.getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_WLAN); |
| }); |
| assertNotNull("Encryption info returned null", info); |
| assertEquals(wlanKey, info.getPublicKey()); |
| assertEquals(TelephonyManager.KEY_TYPE_WLAN, info.getKeyType()); |
| } catch (SecurityException se) { |
| fail("testGetCarrierInfoForImsiEncryption: SecurityException not expected"); |
| } catch (IllegalArgumentException iae) { |
| // IllegalArgumentException is okay, just not SecurityException |
| } |
| } |
| |
| @Test |
| public void testResetCarrierKeysForImsiEncryption() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| // test without permission: verify SecurityException |
| try { |
| mTelephonyManager.resetCarrierKeysForImsiEncryption(); |
| fail("testResetCarrierKeysForImsiEncryption: SecurityException expected"); |
| } catch (SecurityException se) { |
| // expected |
| } |
| // test with permission |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.resetCarrierKeysForImsiEncryption()); |
| } catch (SecurityException se) { |
| fail("testResetCarrierKeysForImsiEncryption: SecurityException not expected"); |
| } |
| } |
| |
| @Test |
| public void testIsInEmergencySmsMode() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)); |
| |
| // test without permission: verify SecurityException |
| try { |
| mTelephonyManager.isInEmergencySmsMode(); |
| fail("testIsInEmergencySmsMode: SecurityException expected"); |
| } catch (SecurityException se) { |
| // expected |
| } |
| // test with permission |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.isInEmergencySmsMode()); |
| } catch (SecurityException se) { |
| fail("testIsInEmergencySmsMode: SecurityException not expected"); |
| } |
| } |
| |
| @Test |
| public void testGetSubscriptionId() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| TelephonyManager tm = mTelephonyManager.createForSubscriptionId(1); |
| int subId = tm.getSubscriptionId(); |
| assertEquals(1, subId); |
| } |
| |
| @Test |
| public void testSetAllowedNetworkTypes() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // test without permission: verify SecurityException |
| long allowedNetworkTypes = TelephonyManager.NETWORK_TYPE_BITMASK_LTE; |
| try { |
| mTelephonyManager.setAllowedNetworkTypes(allowedNetworkTypes); |
| fail("testSetAllowedNetworkTypes: SecurityException expected"); |
| } catch (SecurityException se) { |
| // expected |
| } |
| |
| // test with permission |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setAllowedNetworkTypes(allowedNetworkTypes)); |
| |
| long deviceAllowedNetworkTypes = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> { |
| return tm.getAllowedNetworkTypes(); |
| } |
| ); |
| assertEquals(allowedNetworkTypes, deviceAllowedNetworkTypes); |
| } catch (SecurityException se) { |
| fail("testSetAllowedNetworkTypes: SecurityException not expected"); |
| } |
| } |
| |
| @Test |
| public void testDisAllowedNetworkTypes() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| long allowedNetworkTypes = ~TelephonyManager.NETWORK_TYPE_BITMASK_NR; |
| long networkTypeBitmask = TelephonyManager.NETWORK_TYPE_BITMASK_LTE |
| | TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA; |
| |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER, |
| allowedNetworkTypes)); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER, |
| networkTypeBitmask)); |
| |
| long modemNetworkTypeBitmask = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> { |
| return tm.getAllowedNetworkTypesBitmask(); |
| } |
| ); |
| long radioAccessFamily = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> { |
| return tm.getSupportedRadioAccessFamily(); |
| } |
| ); |
| |
| // RadioAccessFamily won't include all bits of RAFs group, so transfer to preferred |
| // network type instead of using bitmask directly |
| int modemPreferredNetworkType = RadioAccessFamily.getNetworkTypeFromRaf( |
| (int) modemNetworkTypeBitmask); |
| int preferredNetworkType = RadioAccessFamily.getNetworkTypeFromRaf( |
| (int) (networkTypeBitmask & allowedNetworkTypes & radioAccessFamily)); |
| assertEquals(preferredNetworkType, modemPreferredNetworkType); |
| } catch (SecurityException se) { |
| fail("testDisAllowedNetworkTypes: SecurityException not expected"); |
| } |
| } |
| |
| @Test |
| public void testSetAllowedNetworkTypesForReason() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // test without permission: verify SecurityException |
| long allowedNetworkTypes = TelephonyManager.NETWORK_TYPE_BITMASK_LTE; |
| try { |
| mIsAllowedNetworkTypeChanged = true; |
| mTelephonyManager.setAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER, allowedNetworkTypes); |
| fail("testSetAllowedNetworkTypesForReason: SecurityException expected"); |
| } catch (SecurityException se) { |
| // expected |
| } |
| |
| // test with permission |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER, |
| allowedNetworkTypes)); |
| |
| long deviceAllowedNetworkTypes = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> { |
| return tm.getAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER); |
| } |
| ); |
| assertEquals(allowedNetworkTypes, deviceAllowedNetworkTypes); |
| } catch (SecurityException se) { |
| fail("testSetAllowedNetworkTypes: SecurityException not expected"); |
| } |
| } |
| |
| @Test |
| public void testSetAllowedNetworkTypesForReason_carrierPrivileges() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| try { |
| CarrierPrivilegeUtils.withCarrierPrivileges(getContext(), |
| SubscriptionManager.getDefaultSubscriptionId(), |
| () -> { |
| mTelephonyManager.setAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER, |
| TelephonyManager.NETWORK_TYPE_BITMASK_LTE); |
| } |
| ); |
| } catch (SecurityException se) { |
| fail("setAllowedNetworkTypesForReason: SecurityException not expected: " |
| + se.getMessage()); |
| } catch (Exception e) { |
| // withCarrierPrivileges declares a checked Exception, so we must handle it. We don't |
| // expect any exceptions so this is still a failure. |
| Log.e(TAG, "Exception not expected. failing test.", e); |
| fail("CarrierPrivilegeUtils.withCarrierPrivileges: Exception not expected. " |
| + "See error log."); |
| } |
| |
| assertThrows(SecurityException.class, () -> { |
| CarrierPrivilegeUtils.withCarrierPrivileges(getContext(), |
| SubscriptionManager.getDefaultSubscriptionId(), |
| () -> { |
| mTelephonyManager.setAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, |
| TelephonyManager.NETWORK_TYPE_BITMASK_LTE); |
| } |
| ); |
| }); |
| } |
| |
| @Test |
| public void testSetAllowedNetworkTypesForReason_moreReason() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // test without permission: verify SecurityException |
| long allowedNetworkTypes1 = TelephonyManager.NETWORK_TYPE_BITMASK_LTE |
| | TelephonyManager.NETWORK_TYPE_BITMASK_UMTS; |
| long allowedNetworkTypes2 = TelephonyManager.NETWORK_TYPE_BITMASK_LTE; |
| long allowedNetworkTypes3 = TelephonyManager.NETWORK_TYPE_BITMASK_LTE |
| | TelephonyManager.NETWORK_TYPE_BITMASK_HSPA |
| | TelephonyManager.NETWORK_TYPE_BITMASK_UMTS; |
| long allowedNetworkTypes4 = TelephonyManager.NETWORK_TYPE_BITMASK_LTE |
| | TelephonyManager.NETWORK_TYPE_BITMASK_HSPA; |
| |
| try { |
| mIsAllowedNetworkTypeChanged = true; |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER, |
| allowedNetworkTypes1), |
| "android.permission.MODIFY_PHONE_STATE"); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER, |
| allowedNetworkTypes2), |
| "android.permission.MODIFY_PHONE_STATE"); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER, |
| allowedNetworkTypes3), |
| "android.permission.MODIFY_PHONE_STATE"); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, |
| allowedNetworkTypes4), |
| "android.permission.MODIFY_PHONE_STATE"); |
| long deviceAllowedNetworkTypes1 = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, |
| (tm) -> { |
| return tm.getAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER); |
| }, |
| "android.permission.READ_PRIVILEGED_PHONE_STATE" |
| ); |
| long deviceAllowedNetworkTypes2 = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, |
| (tm) -> { |
| return tm.getAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER); |
| }, |
| "android.permission.READ_PRIVILEGED_PHONE_STATE" |
| ); |
| long deviceAllowedNetworkTypes3 = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> { |
| return tm.getAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER); |
| }, |
| "android.permission.READ_PRIVILEGED_PHONE_STATE" |
| ); |
| long deviceAllowedNetworkTypes4 = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> { |
| return tm.getAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G); |
| }, |
| "android.permission.READ_PRIVILEGED_PHONE_STATE" |
| ); |
| assertEquals(allowedNetworkTypes1, deviceAllowedNetworkTypes1); |
| assertEquals(allowedNetworkTypes2, deviceAllowedNetworkTypes2); |
| assertEquals(allowedNetworkTypes3, deviceAllowedNetworkTypes3); |
| assertEquals(allowedNetworkTypes4, deviceAllowedNetworkTypes4); |
| } catch (SecurityException se) { |
| fail("testSetAllowedNetworkTypes: SecurityException not expected"); |
| } |
| } |
| |
| @Test |
| public void testIsApplicationOnUicc() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| // Expect a security exception without permission. |
| try { |
| mTelephonyManager.isApplicationOnUicc(TelephonyManager.APPTYPE_SIM); |
| fail("Expected security exception"); |
| } catch (SecurityException se1) { |
| // Expected |
| } |
| |
| InstrumentationRegistry.getInstrumentation() |
| .getUiAutomation() |
| .adoptShellPermissionIdentity( |
| android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE); |
| try { |
| mTelephonyManager.isApplicationOnUicc(TelephonyManager.APPTYPE_SIM); |
| } catch (SecurityException se) { |
| fail("Caller with READ_PRIVILEGED_PHONE_STATE should be able to call API"); |
| } finally { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .dropShellPermissionIdentity(); |
| } |
| } |
| |
| @Test |
| @ApiTest(apis = {"android.telephony.TelephonyManager#requestModemActivityInfo"}) |
| public void testRequestModemActivityInfo() throws Exception { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .adoptShellPermissionIdentity(android.Manifest.permission.MODIFY_PHONE_STATE); |
| try { |
| // Get one instance of activity info and make sure it's valid |
| CompletableFuture<ModemActivityInfo> future1 = new CompletableFuture<>(); |
| mTelephonyManager.requestModemActivityInfo(getContext().getMainExecutor(), |
| future1::complete); |
| ModemActivityInfo activityInfo1 = future1.get(TOLERANCE, TimeUnit.MILLISECONDS); |
| assertNotNull(activityInfo1); |
| assertTrue("first activity info is" + activityInfo1, activityInfo1.isValid()); |
| |
| // Wait a bit, then get another instance to make sure that some info has accumulated |
| waitForMs(5000); |
| CompletableFuture<ModemActivityInfo> future2 = new CompletableFuture<>(); |
| mTelephonyManager.requestModemActivityInfo(getContext().getMainExecutor(), |
| future2::complete); |
| ModemActivityInfo activityInfo2 = future2.get(TOLERANCE, TimeUnit.MILLISECONDS); |
| assertNotNull(activityInfo2); |
| assertTrue("second activity info is" + activityInfo2, activityInfo2.isValid()); |
| |
| ModemActivityInfo diff = activityInfo1.getDelta(activityInfo2); |
| assertNotNull(diff); |
| assertTrue("two activityInfo are identical", !activityInfo1.equals(activityInfo2)); |
| assertTrue("diff is" + diff, diff.isValid() || diff.isEmpty()); |
| } finally { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .dropShellPermissionIdentity(); |
| } |
| } |
| |
| @Test |
| public void testModemActivityInfoException() { |
| TelephonyManager.ModemActivityInfoException exception = |
| new TelephonyManager.ModemActivityInfoException( |
| TelephonyManager.ModemActivityInfoException.ERROR_PHONE_NOT_AVAILABLE); |
| assertEquals(TelephonyManager.ModemActivityInfoException.ERROR_PHONE_NOT_AVAILABLE, |
| exception.getErrorCode()); |
| } |
| |
| @Test |
| public void testGetSupportedModemCount() { |
| int supportedModemCount = mTelephonyManager.getSupportedModemCount(); |
| int activeModemCount = mTelephonyManager.getActiveModemCount(); |
| assertTrue(activeModemCount >= 0); |
| assertTrue(supportedModemCount >= activeModemCount); |
| } |
| |
| @Test |
| public void testGetAllNetworkTypes() { |
| Set<Integer> expectedNetworkTypes = new HashSet<>(Arrays.asList( |
| TelephonyManager.NETWORK_TYPE_GPRS, |
| TelephonyManager.NETWORK_TYPE_EDGE, |
| TelephonyManager.NETWORK_TYPE_UMTS, |
| TelephonyManager.NETWORK_TYPE_CDMA, |
| TelephonyManager.NETWORK_TYPE_EVDO_0, |
| TelephonyManager.NETWORK_TYPE_EVDO_A, |
| TelephonyManager.NETWORK_TYPE_1xRTT, |
| TelephonyManager.NETWORK_TYPE_HSDPA, |
| TelephonyManager.NETWORK_TYPE_HSUPA, |
| TelephonyManager.NETWORK_TYPE_HSPA, |
| TelephonyManager.NETWORK_TYPE_IDEN, |
| TelephonyManager.NETWORK_TYPE_EVDO_B, |
| TelephonyManager.NETWORK_TYPE_LTE, |
| TelephonyManager.NETWORK_TYPE_EHRPD, |
| TelephonyManager.NETWORK_TYPE_HSPAP, |
| TelephonyManager.NETWORK_TYPE_GSM, |
| TelephonyManager.NETWORK_TYPE_TD_SCDMA, |
| TelephonyManager.NETWORK_TYPE_IWLAN, |
| TelephonyManager.NETWORK_TYPE_LTE_CA, |
| TelephonyManager.NETWORK_TYPE_NR |
| )); |
| |
| Set<Integer> actualNetworkTypes = IntStream.of(TelephonyManager.getAllNetworkTypes()) |
| .boxed().collect(Collectors.toSet()); |
| assertTrue(expectedNetworkTypes.containsAll(actualNetworkTypes)); |
| assertTrue(actualNetworkTypes.containsAll(expectedNetworkTypes)); |
| } |
| |
| @Test |
| public void testIsModemEnabledForSlot() { |
| int activeModemCount = mTelephonyManager.getActiveModemCount(); |
| for (int i = 0; i < activeModemCount; i++) { |
| // Call isModemEnabledForSlot for each slot and verify no crash. |
| mTelephonyManager.isModemEnabledForSlot(i); |
| } |
| } |
| |
| @Test |
| public void testOpportunisticNetworkState() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) |
| && !mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)); |
| |
| boolean isEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| tm -> tm.isOpportunisticNetworkEnabled()); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setOpportunisticNetworkState(true)); |
| assertTrue(ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| tm -> tm.isOpportunisticNetworkEnabled())); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setOpportunisticNetworkState(false)); |
| assertFalse(ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| tm -> tm.isOpportunisticNetworkEnabled())); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setOpportunisticNetworkState(isEnabled)); |
| } |
| |
| @Test |
| public void testGetSimApplicationState() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| int simApplicationState = mTelephonyManager.getSimApplicationState(); |
| assertTrue(Arrays.asList(TelephonyManager.SIM_STATE_UNKNOWN, |
| TelephonyManager.SIM_STATE_PIN_REQUIRED, |
| TelephonyManager.SIM_STATE_PUK_REQUIRED, |
| TelephonyManager.SIM_STATE_NETWORK_LOCKED, |
| TelephonyManager.SIM_STATE_NOT_READY, |
| TelephonyManager.SIM_STATE_PERM_DISABLED, |
| TelephonyManager.SIM_STATE_LOADED).contains(simApplicationState)); |
| |
| for (int i = 0; i <= mTelephonyManager.getPhoneCount(); i++) { |
| final int slotId = i; |
| simApplicationState = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.getSimApplicationState(slotId)); |
| assertTrue(Arrays.asList(TelephonyManager.SIM_STATE_UNKNOWN, |
| TelephonyManager.SIM_STATE_PIN_REQUIRED, |
| TelephonyManager.SIM_STATE_PUK_REQUIRED, |
| TelephonyManager.SIM_STATE_NETWORK_LOCKED, |
| TelephonyManager.SIM_STATE_NOT_READY, |
| TelephonyManager.SIM_STATE_PERM_DISABLED, |
| TelephonyManager.SIM_STATE_LOADED).contains(simApplicationState)); |
| } |
| } |
| |
| @Test |
| public void testGetSimApplicationStateWithPhysicalSlotIndexAndPortIndex() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| try { |
| List<UiccCardInfo> cardInfoList = |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getUiccCardsInfo()); |
| for (UiccCardInfo cardInfo : cardInfoList) { |
| int physicalSlotIndex = cardInfo.getPhysicalSlotIndex(); |
| List<UiccPortInfo> portInfoList = (List<UiccPortInfo>) cardInfo.getPorts(); |
| for (UiccPortInfo uiccPortInfo : portInfoList) { |
| int portIndex = uiccPortInfo.getPortIndex(); |
| int simApplicationState = |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getSimApplicationState(physicalSlotIndex, |
| portIndex)); |
| assertTrue(Arrays.asList(TelephonyManager.SIM_STATE_UNKNOWN, |
| TelephonyManager.SIM_STATE_PIN_REQUIRED, |
| TelephonyManager.SIM_STATE_PUK_REQUIRED, |
| TelephonyManager.SIM_STATE_NETWORK_LOCKED, |
| TelephonyManager.SIM_STATE_NOT_READY, |
| TelephonyManager.SIM_STATE_PERM_DISABLED, |
| TelephonyManager.SIM_STATE_LOADED).contains(simApplicationState)); |
| } |
| } |
| } catch (SecurityException e) { |
| fail("Caller with READ_PRIVILEGED_PHONE_STATE should be able to call API"); |
| } |
| } |
| |
| @Test |
| public void testGetSimCardState() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| int simCardState = mTelephonyManager.getSimCardState(); |
| assertTrue(Arrays.asList(TelephonyManager.SIM_STATE_UNKNOWN, |
| TelephonyManager.SIM_STATE_ABSENT, |
| TelephonyManager.SIM_STATE_CARD_IO_ERROR, |
| TelephonyManager.SIM_STATE_CARD_RESTRICTED, |
| TelephonyManager.SIM_STATE_PRESENT).contains(simCardState)); |
| } |
| @Test |
| @ApiTest(apis = {"android.telephony.TelephonyManager#getUiccCardsInfo", |
| "android.telephony.TelephonyManager#getSimCardState"}) |
| public void getSimCardStateTest() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| InstrumentationRegistry.getInstrumentation() |
| .getUiAutomation() |
| .adoptShellPermissionIdentity( |
| android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE); |
| List<UiccCardInfo> cardsInfo = mTelephonyManager.getUiccCardsInfo(); |
| for (UiccCardInfo cardInfo : cardsInfo) { |
| for (UiccPortInfo portInfo : cardInfo.getPorts()) { |
| int simCardState = mTelephonyManager.getSimCardState(cardInfo |
| .getPhysicalSlotIndex(), portInfo.getPortIndex()); |
| assertTrue(Arrays.asList(TelephonyManager.SIM_STATE_UNKNOWN, |
| TelephonyManager.SIM_STATE_ABSENT, |
| TelephonyManager.SIM_STATE_CARD_IO_ERROR, |
| TelephonyManager.SIM_STATE_CARD_RESTRICTED, |
| TelephonyManager.SIM_STATE_PRESENT).contains(simCardState)); |
| } |
| } |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .dropShellPermissionIdentity(); |
| } |
| |
| @Test |
| public void testMultipleEnabledProfiles() { |
| if (hasFeature(PackageManager.FEATURE_TELEPHONY_EUICC_MEP)) { |
| List<UiccCardInfo> cardInfos = |
| ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.getUiccCardsInfo()); |
| for (UiccCardInfo cardInfo : cardInfos) { |
| // This test suppose there is no use case that OEMs will have multiple esim |
| // chipset with different MEP capabilities. |
| if (cardInfo.isEuicc()) { |
| assertTrue(cardInfo.isMultipleEnabledProfilesSupported()); |
| List<UiccPortInfo> uiccPortInfos = (List<UiccPortInfo>) |
| ShellIdentityUtils.invokeMethodWithShellPermissions(cardInfo, |
| (card) -> card.getPorts()); |
| assertTrue(uiccPortInfos.size() > 1); |
| } |
| } |
| } |
| } |
| |
| private boolean isDataEnabled() { |
| return ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| TelephonyManager::isDataEnabled); |
| } |
| |
| @Test |
| public void testThermalDataEnable() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_DATA)); |
| |
| // Perform this test on default data subscription. |
| mTelephonyManager = getContext().getSystemService(TelephonyManager.class) |
| .createForSubscriptionId(SubscriptionManager.getDefaultDataSubscriptionId()); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_THERMAL, |
| false)); |
| |
| waitForMs(1000); |
| boolean isDataEnabledForReason = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isDataEnabledForReason( |
| TelephonyManager.DATA_ENABLED_REASON_THERMAL)); |
| assertFalse(isDataEnabledForReason); |
| |
| boolean isDataConnectionAvailable = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, TelephonyManager::isDataConnectionAllowed); |
| assertFalse(isDataConnectionAvailable); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_THERMAL, |
| true)); |
| |
| waitForMs(1000); |
| isDataEnabledForReason = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isDataEnabledForReason( |
| TelephonyManager.DATA_ENABLED_REASON_THERMAL)); |
| assertTrue(isDataEnabledForReason); |
| |
| isDataConnectionAvailable = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, TelephonyManager::isDataConnectionAllowed); |
| assertTrue(isDataConnectionAvailable); |
| } |
| |
| @Test |
| public void testPolicyDataEnable() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_DATA)); |
| |
| // Perform this test on default data subscription. |
| mTelephonyManager = getContext().getSystemService(TelephonyManager.class) |
| .createForSubscriptionId(SubscriptionManager.getDefaultDataSubscriptionId()); |
| |
| int retry = 0; |
| boolean isDataEnabledForReason = true; |
| boolean isDataConnectionAvailable = true; |
| // NPMS will set policy data to true after tests set it to false, |
| // so retry disabling policy data to prevent flaky test failures. |
| // TODO: Set empty policies once we can suppress default policies. |
| while ((isDataEnabledForReason || isDataConnectionAvailable) && retry < 30) { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_POLICY, |
| false)); |
| isDataEnabledForReason = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isDataEnabledForReason( |
| TelephonyManager.DATA_ENABLED_REASON_POLICY)); |
| isDataConnectionAvailable = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, TelephonyManager::isDataConnectionAllowed); |
| retry++; |
| waitForMs(500); |
| } |
| assertFalse(isDataEnabledForReason); |
| assertFalse(isDataConnectionAvailable); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_POLICY, |
| true)); |
| |
| waitForMs(1000); |
| isDataEnabledForReason = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isDataEnabledForReason( |
| TelephonyManager.DATA_ENABLED_REASON_POLICY)); |
| assertTrue(isDataEnabledForReason); |
| |
| isDataConnectionAvailable = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, TelephonyManager::isDataConnectionAllowed); |
| assertTrue(isDataConnectionAvailable); |
| } |
| |
| @Test |
| public void testCarrierDataEnable() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_DATA)); |
| |
| // Perform this test on default data subscription. |
| mTelephonyManager = getContext().getSystemService(TelephonyManager.class) |
| .createForSubscriptionId(SubscriptionManager.getDefaultDataSubscriptionId()); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_CARRIER, |
| false)); |
| |
| waitForMs(1000); |
| boolean isDataEnabledForReason = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isDataEnabledForReason( |
| TelephonyManager.DATA_ENABLED_REASON_CARRIER)); |
| assertFalse(isDataEnabledForReason); |
| |
| boolean isDataConnectionAvailable = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, TelephonyManager::isDataConnectionAllowed); |
| assertFalse(isDataConnectionAvailable); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_CARRIER, |
| true)); |
| |
| waitForMs(1000); |
| isDataEnabledForReason = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isDataEnabledForReason( |
| TelephonyManager.DATA_ENABLED_REASON_CARRIER)); |
| assertTrue(isDataEnabledForReason); |
| isDataConnectionAvailable = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, TelephonyManager::isDataConnectionAllowed); |
| assertTrue(isDataConnectionAvailable); |
| } |
| |
| @Test |
| public void testUserDataEnable() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_DATA)); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER, |
| false)); |
| |
| waitForMs(1000); |
| boolean isDataEnabledForReason = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isDataEnabledForReason( |
| TelephonyManager.DATA_ENABLED_REASON_USER)); |
| assertFalse(isDataEnabledForReason); |
| |
| boolean isDataConnectionAvailable = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, TelephonyManager::isDataConnectionAllowed); |
| assertFalse(isDataConnectionAvailable); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER, |
| true)); |
| |
| waitForMs(1000); |
| isDataEnabledForReason = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isDataEnabledForReason( |
| TelephonyManager.DATA_ENABLED_REASON_USER)); |
| assertTrue(isDataEnabledForReason); |
| isDataConnectionAvailable = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, TelephonyManager::isDataConnectionAllowed); |
| assertTrue(isDataConnectionAvailable); |
| } |
| |
| @Test |
| public void testDataDuringVoiceCallPolicy() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_DATA)); |
| |
| ShellIdentityUtils.ShellPermissionMethodHelper<Boolean, TelephonyManager> getPolicyHelper = |
| (tm) -> tm.isMobileDataPolicyEnabled( |
| TelephonyManager.MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL); |
| |
| boolean allowDataDuringVoiceCall = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, getPolicyHelper); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, (tm) -> tm.setMobileDataPolicyEnabled( |
| TelephonyManager.MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL, |
| !allowDataDuringVoiceCall)); |
| |
| waitForMs(500); |
| assertNotEquals(allowDataDuringVoiceCall, |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, getPolicyHelper)); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, (tm) -> tm.setMobileDataPolicyEnabled( |
| TelephonyManager.MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL, |
| allowDataDuringVoiceCall)); |
| |
| waitForMs(500); |
| assertEquals(allowDataDuringVoiceCall, |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, getPolicyHelper)); |
| } |
| |
| private interface Condition { |
| Object expected(); |
| Object actual(); |
| } |
| |
| private void waitUntilConditionIsTrueOrTimeout( |
| Condition condition, long timeout, String description) { |
| final long start = System.currentTimeMillis(); |
| while (!Objects.equals(condition.expected(), condition.actual()) |
| && System.currentTimeMillis() - start < timeout) { |
| waitForMs(50); |
| } |
| assertEquals(description, condition.expected(), condition.actual()); |
| } |
| |
| private void waitForDataPolicySetting(ShellIdentityUtils.ShellPermissionMethodHelper<Boolean, |
| TelephonyManager> getPolicyHelper, boolean mmsAlwaysAllowed) { |
| waitUntilConditionIsTrueOrTimeout( |
| new Condition() { |
| @Override |
| public Object expected() { |
| return mmsAlwaysAllowed; |
| } |
| |
| @Override |
| public Object actual() { |
| Log.d(TAG, "invokeMethodWithShellPermissions : " + mmsAlwaysAllowed); |
| return ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, getPolicyHelper); |
| } |
| }, WAIT_FOR_CONDITION, "Policy returned"); |
| } |
| |
| @Test |
| public void testAlwaysAllowMmsDataPolicy() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_DATA)); |
| |
| ShellIdentityUtils.ShellPermissionMethodHelper<Boolean, TelephonyManager> getPolicyHelper = |
| (tm) -> tm.isMobileDataPolicyEnabled( |
| TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED); |
| |
| boolean mmsAlwaysAllowed = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, getPolicyHelper); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, (tm) -> tm.setMobileDataPolicyEnabled( |
| TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED, |
| !mmsAlwaysAllowed)); |
| |
| waitForDataPolicySetting(getPolicyHelper, !mmsAlwaysAllowed); |
| assertNotEquals(mmsAlwaysAllowed, |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, getPolicyHelper)); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, (tm) -> tm.setMobileDataPolicyEnabled( |
| TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED, |
| mmsAlwaysAllowed)); |
| |
| waitForDataPolicySetting(getPolicyHelper, mmsAlwaysAllowed); |
| assertEquals(mmsAlwaysAllowed, |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, getPolicyHelper)); |
| } |
| |
| @Test |
| public void testAutoDataSwitchPolicy() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_DATA)); |
| |
| ShellIdentityUtils.ShellPermissionMethodHelper<Boolean, TelephonyManager> getPolicyHelper = |
| (tm) -> tm.isMobileDataPolicyEnabled( |
| TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH); |
| |
| boolean autoDatSwitchAllowed = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, getPolicyHelper); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, (tm) -> tm.setMobileDataPolicyEnabled( |
| TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, |
| !autoDatSwitchAllowed)); |
| |
| waitForMs(1000); |
| assertNotEquals(autoDatSwitchAllowed, |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, getPolicyHelper)); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, (tm) -> tm.setMobileDataPolicyEnabled( |
| TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, |
| autoDatSwitchAllowed)); |
| |
| waitForMs(1000); |
| assertEquals(autoDatSwitchAllowed, |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, getPolicyHelper)); |
| } |
| |
| @Test |
| public void testGetCdmaEnhancedRoamingIndicatorDisplayNumber() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_CDMA)); |
| |
| int index = mTelephonyManager.getCdmaEnhancedRoamingIndicatorDisplayNumber(); |
| int phoneType = mTelephonyManager.getPhoneType(); |
| if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) { |
| assertTrue(index >= 0 && index <= 255); |
| } else { |
| assertEquals(-1, index); |
| } |
| } |
| |
| private int disableNrDualConnectivity() { |
| if (!ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isRadioInterfaceCapabilitySupported( |
| TelephonyManager |
| .CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE))) { |
| return TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED; |
| } |
| |
| int result = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, |
| (tm) -> tm.setNrDualConnectivityState( |
| TelephonyManager.NR_DUAL_CONNECTIVITY_DISABLE)); |
| |
| boolean isNrDualConnectivityEnabled = |
| ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isNrDualConnectivityEnabled()); |
| // Only verify the result for supported devices on IRadio 1.6+ |
| if (mNetworkHalVersion >= RADIO_HAL_VERSION_1_6 |
| && result != TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED) { |
| assertFalse(isNrDualConnectivityEnabled); |
| } |
| |
| return result; |
| } |
| |
| @Test |
| public void testNrDualConnectivityEnable() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| if (!ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isRadioInterfaceCapabilitySupported( |
| TelephonyManager |
| .CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE))) { |
| return; |
| } |
| |
| boolean isInitiallyEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isNrDualConnectivityEnabled()); |
| boolean isNrDualConnectivityEnabled; |
| int result; |
| if (isInitiallyEnabled) { |
| result = disableNrDualConnectivity(); |
| if (result == TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED) { |
| return; |
| } |
| } |
| |
| |
| result = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, |
| (tm) -> tm.setNrDualConnectivityState( |
| TelephonyManager.NR_DUAL_CONNECTIVITY_ENABLE)); |
| |
| if (result == TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED) { |
| return; |
| } |
| |
| isNrDualConnectivityEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isNrDualConnectivityEnabled()); |
| // Only verify the result for supported devices on IRadio 1.6+ |
| if (mNetworkHalVersion >= RADIO_HAL_VERSION_1_6) { |
| assertTrue(isNrDualConnectivityEnabled); |
| } |
| |
| if (!isInitiallyEnabled) { |
| disableNrDualConnectivity(); |
| } |
| } |
| |
| @Test |
| public void testCdmaRoamingMode() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_CDMA) |
| && mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA); |
| |
| // Save state |
| int cdmaRoamingMode = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| TelephonyManager::getCdmaRoamingMode); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setCdmaRoamingMode(TelephonyManager.CDMA_ROAMING_MODE_HOME)); |
| assertEquals(TelephonyManager.CDMA_ROAMING_MODE_HOME, |
| (int) ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| TelephonyManager::getCdmaRoamingMode)); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setCdmaRoamingMode(TelephonyManager.CDMA_ROAMING_MODE_AFFILIATED)); |
| assertEquals(TelephonyManager.CDMA_ROAMING_MODE_AFFILIATED, |
| (int) ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| TelephonyManager::getCdmaRoamingMode)); |
| |
| // Reset state |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setCdmaRoamingMode(cdmaRoamingMode)); |
| } |
| |
| @Test |
| public void testCdmaSubscriptionMode() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_CDMA) |
| && mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA); |
| |
| // Save state |
| int cdmaSubscriptionMode = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, TelephonyManager::getCdmaSubscriptionMode); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setCdmaSubscriptionMode(TelephonyManager.CDMA_SUBSCRIPTION_NV)); |
| assertEquals(TelephonyManager.CDMA_SUBSCRIPTION_NV, |
| (int) ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| TelephonyManager::getCdmaSubscriptionMode)); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setCdmaSubscriptionMode(TelephonyManager.CDMA_SUBSCRIPTION_RUIM_SIM)); |
| assertEquals(TelephonyManager.CDMA_SUBSCRIPTION_RUIM_SIM, |
| (int) ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| TelephonyManager::getCdmaSubscriptionMode)); |
| |
| // Reset state |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setCdmaSubscriptionMode(cdmaSubscriptionMode)); |
| } |
| |
| @Test |
| public void testPinResult() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| final String empty_pin = ""; // For getting current remaining pin attempt. |
| final String pin = "fake_pin"; |
| final String puk = "fake_puk"; |
| final String newPin = "fake_new_pin"; |
| |
| //Refer GSM 02.17 5.6 PIN Management |
| //To avoid that sim may enter PUK state, |
| //TC should be allowed when current Pin attempt count is reset with 3. |
| boolean isEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, TelephonyManager::isIccLockEnabled); |
| PinResult result = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.supplyIccLockPin(empty_pin)); |
| if (result.getAttemptsRemaining() < 3) { |
| Log.d(TAG, "Skipping test and requires that reboot device and unlock pin successfully"); |
| return; |
| } |
| |
| result = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.setIccLockEnabled(!isEnabled, pin)); |
| assertTrue(result.getResult() == PinResult.PIN_RESULT_TYPE_INCORRECT |
| || result.getResult() == PinResult.PIN_RESULT_TYPE_FAILURE); |
| assertTrue(result.getAttemptsRemaining() >= -1); |
| assertEquals(isEnabled, ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, TelephonyManager::isIccLockEnabled)); |
| |
| result = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.changeIccLockPin(pin, newPin)); |
| assertTrue(result.getResult() == PinResult.PIN_RESULT_TYPE_INCORRECT |
| || result.getResult() == PinResult.PIN_RESULT_TYPE_FAILURE); |
| assertTrue(result.getAttemptsRemaining() >= -1); |
| |
| result = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.supplyIccLockPin(pin)); |
| assertTrue(result.getResult() == PinResult.PIN_RESULT_TYPE_INCORRECT |
| || result.getResult() == PinResult.PIN_RESULT_TYPE_FAILURE); |
| assertTrue(result.getAttemptsRemaining() >= -1); |
| |
| result = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.supplyIccLockPuk(puk, pin)); |
| assertTrue(result.getResult() == PinResult.PIN_RESULT_TYPE_INCORRECT |
| || result.getResult() == PinResult.PIN_RESULT_TYPE_FAILURE); |
| assertTrue(result.getAttemptsRemaining() >= -1); |
| } |
| |
| @Test |
| public void testSetSignalStrengthUpdateRequest_nullRequest() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // Verify NPE throws if set request with null object |
| try { |
| mTelephonyManager.setSignalStrengthUpdateRequest(null); |
| fail("NullPointerException expected when setSignalStrengthUpdateRequest with null"); |
| } catch (NullPointerException expected) { |
| } |
| } |
| |
| @Test |
| public void testSetSignalStrengthUpdateRequest_noPermission() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| final SignalStrengthUpdateRequest normalRequest = |
| new SignalStrengthUpdateRequest.Builder() |
| .setSignalThresholdInfos(List.of( |
| new SignalThresholdInfo.Builder() |
| .setRadioAccessNetworkType( |
| AccessNetworkConstants.AccessNetworkType.GERAN) |
| .setSignalMeasurementType( |
| SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI) |
| .setThresholds(new int[]{-113, -103, -97, -51}) |
| .build())) |
| .setReportingRequestedWhileIdle(true) |
| .build(); |
| |
| // Verify SE throws for apps without carrier privilege or MODIFY_PHONE_STATE permission |
| try { |
| mTelephonyManager.setSignalStrengthUpdateRequest(normalRequest); |
| fail("SecurityException expected when setSignalStrengthUpdateRequest without " |
| + "carrier privilege or MODIFY_PHONE_STATE permission"); |
| } catch (SecurityException expected) { |
| } finally { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.clearSignalStrengthUpdateRequest(normalRequest)); |
| } |
| } |
| |
| @Test |
| public void testSetSignalStrengthUpdateRequest_systemThresholdReportingRequestedWhileIdle() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // Verify system privileged app with permission LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH can |
| // set systemThresholdReportingRequestedWhileIdle to true with empty thresholdInfos |
| SignalStrengthUpdateRequest request = new SignalStrengthUpdateRequest.Builder() |
| .setSignalThresholdInfos(Collections.EMPTY_LIST) |
| .setSystemThresholdReportingRequestedWhileIdle(true) |
| .build(); |
| |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, (tm) -> tm.setSignalStrengthUpdateRequest(request)); |
| } finally { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.clearSignalStrengthUpdateRequest(request)); |
| } |
| } |
| |
| @Test |
| public void testSetSignalStrengthUpdateRequest_hysteresisDbSet() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // Verify SE throws for app when set hysteresisDb in the SignalThresholdInfo |
| SignalStrengthUpdateRequest requestWithHysteresisDbSet = |
| new SignalStrengthUpdateRequest.Builder() |
| .setSignalThresholdInfos(List.of( |
| new SignalThresholdInfo.Builder() |
| .setRadioAccessNetworkType( |
| AccessNetworkConstants.AccessNetworkType.GERAN) |
| .setSignalMeasurementType( |
| SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI) |
| .setThresholds(new int[]{-113, -103, -97, -51}) |
| .setHysteresisDb(10) |
| .build())) |
| .setReportingRequestedWhileIdle(true) |
| .build(); |
| |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setSignalStrengthUpdateRequest(requestWithHysteresisDbSet)); |
| } finally { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.clearSignalStrengthUpdateRequest(requestWithHysteresisDbSet)); |
| } |
| } |
| |
| |
| @Test |
| public void testSetSignalStrengthUpdateRequest_hysteresisMsSet() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // Verify SE throws for app when set hysteresisMs in the SignalThresholdInfo |
| SignalStrengthUpdateRequest requestWithHysteresisMsSet = |
| new SignalStrengthUpdateRequest.Builder() |
| .setSignalThresholdInfos(List.of( |
| new SignalThresholdInfo.Builder() |
| .setRadioAccessNetworkType( |
| AccessNetworkConstants.AccessNetworkType.GERAN) |
| .setSignalMeasurementType( |
| SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI) |
| .setThresholds(new int[]{-113, -103, -97, -51}) |
| .setHysteresisMs(1000) //allowed for system caller only |
| .build())) |
| .setReportingRequestedWhileIdle(true) |
| .build(); |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setSignalStrengthUpdateRequest(requestWithHysteresisMsSet)); |
| fail("IllegalArgumentException expected when set hysteresisMs in SignalThresholdInfo " |
| + "to true"); |
| } catch (IllegalArgumentException expected) { |
| } finally { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.clearSignalStrengthUpdateRequest(requestWithHysteresisMsSet)); |
| } |
| } |
| |
| @Test |
| public void testSetSignalStrengthUpdateRequest_isEnabledSet() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // Verify SE throws for app when set isEnabled in the SignalThresholdInfo |
| SignalStrengthUpdateRequest requestWithThresholdIsEnabledSet = |
| new SignalStrengthUpdateRequest.Builder() |
| .setSignalThresholdInfos(List.of( |
| new SignalThresholdInfo.Builder() |
| .setRadioAccessNetworkType( |
| AccessNetworkConstants.AccessNetworkType.GERAN) |
| .setSignalMeasurementType( |
| SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI) |
| .setThresholds(new int[]{-113, -103, -97}) |
| .setIsEnabled(true) //allowed for system caller only |
| .build())) |
| .setReportingRequestedWhileIdle(true) |
| .build(); |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setSignalStrengthUpdateRequest(requestWithThresholdIsEnabledSet)); |
| fail("IllegalArgumentException expected when set isEnabled in SignalThresholdInfo " |
| + "with true"); |
| } catch (IllegalArgumentException expected) { |
| } finally { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.clearSignalStrengthUpdateRequest(requestWithThresholdIsEnabledSet)); |
| } |
| } |
| |
| @Test |
| public void testSetSignalStrengthUpdateRequest_tooShortThresholds() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // verify SE throws if app set too short thresholds |
| SignalStrengthUpdateRequest requestWithTooShortThresholds = |
| new SignalStrengthUpdateRequest.Builder() |
| .setSignalThresholdInfos(List.of( |
| new SignalThresholdInfo.Builder() |
| .setRadioAccessNetworkType( |
| AccessNetworkConstants.AccessNetworkType.GERAN) |
| .setSignalMeasurementType( |
| SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI) |
| .setThresholds(new int[]{}, true /*isSystem*/) |
| .build())) |
| .setReportingRequestedWhileIdle(true) |
| .build(); |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setSignalStrengthUpdateRequest(requestWithTooShortThresholds)); |
| fail("IllegalArgumentException expected when set thresholds that is too short"); |
| } catch (IllegalArgumentException expected) { |
| } finally { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.clearSignalStrengthUpdateRequest(requestWithTooShortThresholds)); |
| } |
| } |
| |
| @Test |
| public void testSetSignalStrengthUpdateRequest_tooLongThresholds() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // verify SE throws if app set too long thresholds |
| SignalStrengthUpdateRequest requestWithTooLongThresholds = |
| new SignalStrengthUpdateRequest.Builder() |
| .setSignalThresholdInfos(List.of( |
| new SignalThresholdInfo.Builder() |
| .setRadioAccessNetworkType( |
| AccessNetworkConstants.AccessNetworkType.GERAN) |
| .setSignalMeasurementType( |
| SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI) |
| .setThresholds(new int[]{-113, -103, -97, -61, -51}, |
| true /*isSystem*/) |
| .build())) |
| .setReportingRequestedWhileIdle(true) |
| .build(); |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setSignalStrengthUpdateRequest(requestWithTooLongThresholds)); |
| fail("IllegalArgumentException expected when set thresholds that is too long"); |
| } catch (IllegalArgumentException expected) { |
| } finally { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.clearSignalStrengthUpdateRequest(requestWithTooLongThresholds)); |
| } |
| } |
| |
| @Test |
| public void testSetSignalStrengthUpdateRequest_duplicatedRequest() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| final SignalStrengthUpdateRequest normalRequest = |
| new SignalStrengthUpdateRequest.Builder() |
| .setSignalThresholdInfos(List.of( |
| new SignalThresholdInfo.Builder() |
| .setRadioAccessNetworkType( |
| AccessNetworkConstants.AccessNetworkType.GERAN) |
| .setSignalMeasurementType( |
| SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI) |
| .setThresholds(new int[]{-113, -103, -97, -51}) |
| .build())) |
| .setReportingRequestedWhileIdle(true) |
| .build(); |
| |
| // Verify IllegalStateException should throw when set the same request twice |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setSignalStrengthUpdateRequest(normalRequest)); |
| try { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setSignalStrengthUpdateRequest(normalRequest)); |
| fail("IllegalStateException expected when setSignalStrengthUpdateRequest twice with " |
| + "same request object"); |
| } catch (IllegalStateException expected) { |
| } finally { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.clearSignalStrengthUpdateRequest(normalRequest)); |
| } |
| } |
| |
| @Test |
| public void testClearSignalStrengthUpdateRequest_nullRequest() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // Verify NPE should throw if clear request with null object |
| try { |
| mTelephonyManager.clearSignalStrengthUpdateRequest(null); |
| fail("NullPointerException expected when clearSignalStrengthUpdateRequest with null"); |
| } catch (NullPointerException expected) { |
| } |
| } |
| |
| @Test |
| public void testClearSignalStrengthUpdateRequest_noPermission() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| final SignalStrengthUpdateRequest normalRequest = |
| new SignalStrengthUpdateRequest.Builder() |
| .setSignalThresholdInfos(List.of( |
| new SignalThresholdInfo.Builder() |
| .setRadioAccessNetworkType( |
| AccessNetworkConstants.AccessNetworkType.GERAN) |
| .setSignalMeasurementType( |
| SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI) |
| .setThresholds(new int[]{-113, -103, -97, -51}) |
| .build())) |
| .setReportingRequestedWhileIdle(true) |
| .build(); |
| |
| // Verify SE throws for apps without carrier privilege or MODIFY_PHONE_STATE permission |
| try { |
| mTelephonyManager.clearSignalStrengthUpdateRequest(normalRequest); |
| fail("SecurityException expected when clearSignalStrengthUpdateRequest without " |
| + "carrier privilege or MODIFY_PHONE_STATE permission"); |
| } catch (SecurityException expected) { |
| } |
| } |
| |
| @Test |
| public void testClearSignalStrengthUpdateRequest_clearWithNoSet() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| SignalStrengthUpdateRequest requestNeverSetBefore = new SignalStrengthUpdateRequest |
| .Builder() |
| .setSignalThresholdInfos(List.of(new SignalThresholdInfo.Builder() |
| .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN) |
| .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI) |
| .setThresholds(new int[]{-113, -103, -97, -51}) |
| .build())) |
| .setReportingRequestedWhileIdle(true) |
| .build(); |
| |
| // Verify clearSignalStrengthUpdateRequest is no-op when clear request that was not set |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.clearSignalStrengthUpdateRequest(requestNeverSetBefore)); |
| } |
| |
| @Test |
| public void testSendThermalMitigationRequest() throws Exception { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| StringBuilder cmdBuilder = new StringBuilder(); |
| cmdBuilder.append(THERMAL_MITIGATION_COMMAND_BASE).append(ALLOW_PACKAGE_SUBCOMMAND) |
| .append(TELEPHONY_CTS_PACKAGE); |
| TelephonyUtils.executeShellCommand(InstrumentationRegistry.getInstrumentation(), |
| cmdBuilder.toString()); |
| |
| long arbitraryCompletionWindowMillis = 60000L; |
| |
| boolean isDataThrottlingSupported = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.isRadioInterfaceCapabilitySupported( |
| TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING)); |
| |
| int thermalMitigationResult = -1; |
| if (isDataThrottlingSupported) { |
| // Test a proper data throttling thermal mitigation request. |
| thermalMitigationResult = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.sendThermalMitigationRequest( |
| new ThermalMitigationRequest.Builder() |
| .setThermalMitigationAction(ThermalMitigationRequest |
| .THERMAL_MITIGATION_ACTION_DATA_THROTTLING) |
| .setDataThrottlingRequest(new DataThrottlingRequest.Builder() |
| .setDataThrottlingAction(DataThrottlingRequest |
| .DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER) |
| .setCompletionDurationMillis(arbitraryCompletionWindowMillis) |
| .build()) |
| .build())); |
| |
| assertEquals(thermalMitigationResult, |
| TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS); |
| } |
| // Test negative completionDurationSecs is an invalid parameter. |
| try { |
| thermalMitigationResult = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.sendThermalMitigationRequest( |
| new ThermalMitigationRequest.Builder() |
| .setThermalMitigationAction(ThermalMitigationRequest |
| .THERMAL_MITIGATION_ACTION_DATA_THROTTLING) |
| .setDataThrottlingRequest(new DataThrottlingRequest.Builder() |
| .setDataThrottlingAction(DataThrottlingRequest |
| .DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER |
| ) |
| .setCompletionDurationMillis(-1) |
| .build()) |
| .build())); |
| } catch (IllegalArgumentException e) { |
| } |
| |
| // Test non-zero completionDurationSecs is an invalid parameter for data throttling hold. |
| try { |
| thermalMitigationResult = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.sendThermalMitigationRequest( |
| new ThermalMitigationRequest.Builder() |
| .setThermalMitigationAction(ThermalMitigationRequest |
| .THERMAL_MITIGATION_ACTION_DATA_THROTTLING) |
| .setDataThrottlingRequest(new DataThrottlingRequest.Builder() |
| .setDataThrottlingAction( |
| DataThrottlingRequest |
| .DATA_THROTTLING_ACTION_HOLD) |
| .setCompletionDurationMillis( |
| arbitraryCompletionWindowMillis) |
| .build()) |
| .build())); |
| } catch (IllegalArgumentException e) { |
| } |
| |
| // Test null DataThrottlingParams is an invalid parameter for data throttling request. |
| try { |
| thermalMitigationResult = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.sendThermalMitigationRequest( |
| new ThermalMitigationRequest.Builder() |
| .setThermalMitigationAction(ThermalMitigationRequest |
| .THERMAL_MITIGATION_ACTION_DATA_THROTTLING) |
| .build())); |
| } catch (IllegalArgumentException e) { |
| } |
| |
| // Test non-null DataThrottlingParams is an invalid parameter for voice only request. |
| try { |
| thermalMitigationResult = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.sendThermalMitigationRequest( |
| new ThermalMitigationRequest.Builder() |
| .setThermalMitigationAction( |
| ThermalMitigationRequest |
| .THERMAL_MITIGATION_ACTION_VOICE_ONLY) |
| .setDataThrottlingRequest(new DataThrottlingRequest.Builder() |
| .setDataThrottlingAction( |
| DataThrottlingRequest |
| .DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER |
| ) |
| .setCompletionDurationMillis(-1) |
| .build()) |
| .build())); |
| } catch (IllegalArgumentException e) { |
| } |
| |
| // Test non-null DataThrottlingParams is an invalid parameter for radio off request. |
| try { |
| thermalMitigationResult = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.sendThermalMitigationRequest( |
| new ThermalMitigationRequest.Builder() |
| .setThermalMitigationAction( |
| ThermalMitigationRequest |
| .THERMAL_MITIGATION_ACTION_RADIO_OFF) |
| .setDataThrottlingRequest(new DataThrottlingRequest.Builder() |
| .setDataThrottlingAction(DataThrottlingRequest |
| .DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER |
| ) |
| .setCompletionDurationMillis(-1) |
| .build()) |
| .build())); |
| } catch (IllegalArgumentException e) { |
| } |
| } |
| |
| @Test |
| public void testIsRadioInterfaceCapabilitySupported() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| assertFalse(mTelephonyManager.isRadioInterfaceCapabilitySupported("empty")); |
| assertFalse(mTelephonyManager.isRadioInterfaceCapabilitySupported(null)); |
| assertFalse(mTelephonyManager.isRadioInterfaceCapabilitySupported("")); |
| } |
| |
| private Set<CellIdentity> getRegisteredCellIdentities() { |
| ServiceState ss = mTelephonyManager.getServiceState(); |
| Set<CellIdentity> cidSet = new ArraySet<>(2); |
| for (NetworkRegistrationInfo nri : ss.getNetworkRegistrationInfoListForTransportType( |
| AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) { |
| if (nri.isRegistered()) cidSet.add(nri.getCellIdentity()); |
| } |
| return cidSet; |
| } |
| |
| private boolean hasMultipleRegisteredSubscriptions() { |
| final int[] activeSubIds = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mSubscriptionManager, (sm) ->sm.getActiveSubscriptionIdList()); |
| int registeredSubscriptions = 0; |
| for (int subId : activeSubIds) { |
| ServiceState ss = mTelephonyManager.createForSubscriptionId(subId).getServiceState(); |
| for (NetworkRegistrationInfo nri : ss.getNetworkRegistrationInfoListForTransportType( |
| AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) { |
| if (nri.isRegistered()) { |
| registeredSubscriptions++; |
| break; |
| } |
| } |
| } |
| return registeredSubscriptions > 1; |
| } |
| |
| @Test |
| public void testGetAllCellInfo() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| // For INetworkRadio <1.5, just verify that calling the method doesn't throw an error. |
| if (mNetworkHalVersion < RADIO_HAL_VERSION_1_5) { |
| mTelephonyManager.getAllCellInfo(); |
| return; |
| } |
| |
| List<CellInfo> allCellInfo = mTelephonyManager.getAllCellInfo(); |
| assertTrue(!allCellInfo.isEmpty()); |
| for (CellInfo cellInfo : allCellInfo) { |
| CellIdentity cellIdentity = cellInfo.getCellIdentity(); |
| int[] bands; |
| if (cellIdentity instanceof CellIdentityLte) { |
| bands = ((CellIdentityLte) cellIdentity).getBands(); |
| if (cellInfo.isRegistered()) assertTrue(bands.length > 0); |
| for (int band : bands) { |
| assertTrue(band >= AccessNetworkConstants.EutranBand.BAND_1 |
| && band <= AccessNetworkConstants.EutranBand.BAND_88); |
| } |
| } else if (cellIdentity instanceof CellIdentityNr) { |
| bands = ((CellIdentityNr) cellIdentity).getBands(); |
| if (cellInfo.isRegistered()) assertTrue(bands.length > 0); |
| for (int band : bands) { |
| assertTrue((band >= AccessNetworkConstants.NgranBands.BAND_1 |
| && band <= AccessNetworkConstants.NgranBands.BAND_95) |
| || (band >= AccessNetworkConstants.NgranBands.BAND_257 |
| && band <= AccessNetworkConstants.NgranBands.BAND_261)); |
| } |
| } |
| |
| // TODO(229311863): This can theoretically break on a DSDS device where both SIMs are |
| // registered because CellInfo returns data for both modems and this code only cross |
| // checks against the default subscription. |
| if (hasMultipleRegisteredSubscriptions()) continue; |
| |
| boolean isSameCell = false; |
| if (cellInfo.isRegistered()) { |
| for (CellIdentity cid : getRegisteredCellIdentities()) { |
| if (cellIdentity.isSameCell(cid)) isSameCell = true; |
| } |
| assertTrue(sNetworkTypes.get(cellIdentity.getClass()).contains( |
| mTelephonyManager.getDataNetworkType()) |
| || sNetworkTypes.get(cellIdentity.getClass()).contains( |
| mTelephonyManager.getVoiceNetworkType())); |
| assertTrue( |
| "Registered CellInfo#CellIdentity not found in ServiceState", |
| isSameCell); |
| } |
| } |
| |
| } |
| |
| @Test |
| @ApiTest(apis = {"android.telephony.CarrierConfigManager#KEY_CARRIER_METERED_APN_TYPES_STRINGS", |
| "android.telephony.CarrierConfigManager#KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS", |
| "android.telephony.TelephonyManager#isApnMetered"}) |
| public void testIsApnMetered() throws Exception { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| PersistableBundle carrierConfig = new PersistableBundle(); |
| carrierConfig.putStringArray( |
| CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS, |
| new String[] {ApnSetting.TYPE_MMS_STRING}); |
| carrierConfig.putStringArray( |
| CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS, |
| new String[] {ApnSetting.TYPE_MMS_STRING}); |
| overrideCarrierConfig(carrierConfig); |
| |
| try { |
| InstrumentationRegistry.getInstrumentation() |
| .getUiAutomation() |
| .adoptShellPermissionIdentity( |
| android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE); |
| PollingCheck.waitFor(5000, () -> !mTelephonyManager.isApnMetered(ApnSetting.TYPE_DUN), |
| "Timeout when waiting for DUN APN to become unmetered"); |
| |
| assertTrue(mTelephonyManager.isApnMetered(ApnSetting.TYPE_MMS)); |
| assertFalse(mTelephonyManager.isApnMetered(ApnSetting.TYPE_DUN)); |
| } finally { |
| // Restore the original carrier config |
| overrideCarrierConfig(null); |
| // Revoke the permission READ_PRIVILEGED_PHONE_STATE |
| InstrumentationRegistry.getInstrumentation() |
| .getUiAutomation() |
| .dropShellPermissionIdentity(); |
| } |
| |
| carrierConfig.putStringArray( |
| CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS, |
| new String[] {ApnSetting.TYPE_DUN_STRING}); |
| carrierConfig.putStringArray( |
| CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS, |
| new String[] {ApnSetting.TYPE_DUN_STRING}); |
| overrideCarrierConfig(carrierConfig); |
| try { |
| InstrumentationRegistry.getInstrumentation() |
| .getUiAutomation() |
| .adoptShellPermissionIdentity( |
| android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE); |
| PollingCheck.waitFor(5000, () -> mTelephonyManager.isApnMetered(ApnSetting.TYPE_DUN), |
| "Timeout when waiting for DUN APN to become metered"); |
| |
| assertFalse(mTelephonyManager.isApnMetered(ApnSetting.TYPE_MMS)); |
| assertTrue(mTelephonyManager.isApnMetered(ApnSetting.TYPE_DUN)); |
| } finally { |
| overrideCarrierConfig(null); |
| InstrumentationRegistry.getInstrumentation() |
| .getUiAutomation() |
| .dropShellPermissionIdentity(); |
| } |
| } |
| |
| /** |
| * Validate Emergency Number address that only contains the dialable character. |
| * |
| * @param address Emergency number address to validate |
| * @return {@code true} if the address is valid; {@code false} otherwise. |
| */ |
| private static boolean validateEmergencyNumberAddress(String address) { |
| if (address == null) { |
| return false; |
| } |
| for (char c : address.toCharArray()) { |
| if (!isDialable(c)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Validate Emergency Number country Iso |
| * |
| * @param countryIso Emergency number country iso to validate |
| * @return {@code true} if the country iso is valid; {@code false} otherwise. |
| */ |
| private static boolean validateEmergencyNumberCountryIso(String countryIso) { |
| if (countryIso == null) { |
| return false; |
| } |
| int length = countryIso.length(); |
| return length >= 0 && length <= 2; |
| } |
| |
| /** |
| * Validate Emergency Number MNC |
| * |
| * @param mnc Emergency number MNC to validate |
| * @return {@code true} if the MNC is valid; {@code false} otherwise. |
| */ |
| private static boolean validateEmergencyNumberMnc(String mnc) { |
| if (mnc == null) { |
| return false; |
| } |
| int length = mnc.length(); |
| return length >= 0 && length <= 3; |
| } |
| |
| /** |
| * Validate Emergency service category list |
| * |
| * @param categories Emergency service category list to validate |
| * @return {@code true} if the category list is valid; {@code false} otherwise. |
| */ |
| private static boolean validateEmergencyServiceCategoryList(List<Integer> categories) { |
| if (categories == null) { |
| return false; |
| } |
| if (categories.contains(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED)) { |
| return categories.size() == 1; |
| } |
| for (int category : categories) { |
| if (!EMERGENCY_SERVICE_CATEGORY_SET.contains(category)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Validate Emergency number source list |
| * |
| * @param categories Emergency number source list to validate |
| * @return {@code true} if the source list is valid; {@code false} otherwise. |
| */ |
| private static boolean validateEmergencyNumberSourceList(List<Integer> sources) { |
| if (sources == null) { |
| return false; |
| } |
| for (int source : sources) { |
| if (!EMERGENCY_NUMBER_SOURCE_SET.contains(source)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Validate Emergency call routing. |
| * |
| * @param routing Emergency call routing to validate |
| * @return {@code true} if the emergency call routing is valid; {@code false} otherwise. |
| */ |
| private static boolean validateEmergencyCallRouting(int routing) { |
| return routing >= EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN |
| && routing <= (EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY |
| | EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL); |
| } |
| |
| /** |
| * Valid the emergency number should be at least from a valid source. |
| * |
| * @param emergencyNumber Emergency number to verify |
| * @return {@code true} if the emergency number is from any source; {@code false} otherwise. |
| */ |
| private static boolean validateEmergencyNumberFromAnySource(EmergencyNumber emergencyNumber) { |
| boolean isFromAnySource = false; |
| for (int possibleSourceValue = EMERGENCY_NUMBER_SOURCE_RIL_ECCLIST; |
| possibleSourceValue <= (EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING |
| | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM |
| | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE |
| | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG |
| | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT); |
| possibleSourceValue++) { |
| if (emergencyNumber.isFromSources(possibleSourceValue)) { |
| isFromAnySource = true; |
| break; |
| } |
| } |
| return isFromAnySource; |
| } |
| |
| /** |
| * Valid the emergency number should be at least in a valid category. |
| * |
| * @param emergencyNumber Emergency number to verify |
| * @return {@code true} if it is in any category; {@code false} otherwise. |
| */ |
| private static boolean validateEmergencyNumberInAnyCategory(EmergencyNumber emergencyNumber) { |
| boolean isInAnyCategory = false; |
| for (int possibleCategoryValue = EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED; |
| possibleCategoryValue <= (EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE |
| | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE |
| | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE |
| | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD |
| | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE |
| | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC |
| | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC); |
| possibleCategoryValue++) { |
| if (emergencyNumber.isInEmergencyServiceCategories(possibleCategoryValue)) { |
| isInAnyCategory = true; |
| break; |
| } |
| } |
| return isInAnyCategory; |
| } |
| |
| @SuppressWarnings("SelfComparison") // TODO: Fix me |
| private static boolean validateEmergencyNumberCompareTo( |
| List<EmergencyNumber> emergencyNumberList) { |
| if (emergencyNumberList == null) { |
| return false; |
| } |
| if (emergencyNumberList.size() > 0) { |
| EmergencyNumber emergencyNumber = emergencyNumberList.get(0); |
| if (emergencyNumber.compareTo(emergencyNumber) != 0) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private static boolean isDialable(char c) { |
| return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == 'N'; |
| } |
| |
| private Map.Entry<Integer, Integer> getValidSlotIndexAndPort() { |
| return ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> { |
| |
| List<UiccCardInfo> cardInfos = mTelephonyManager.getUiccCardsInfo(); |
| Set<String> presentCards = Arrays.stream(mTelephonyManager.getUiccSlotsInfo()) |
| .filter(Objects::nonNull) |
| .filter(port -> port.getPorts().stream().anyMatch(portInfo -> |
| portInfo.isActive())) |
| .map(UiccSlotInfo::getCardId) |
| .filter(Objects::nonNull) |
| // hack around getUiccSlotsInfo not stripping trailing F |
| .map(s -> s.endsWith("F") ? s.substring(0, s.length() - 1) : s) |
| .collect(Collectors.toSet()); |
| int slotIndex = -1; |
| int portIndex = -1; |
| for (UiccCardInfo cardInfo : cardInfos) { |
| for (UiccPortInfo portInfo : cardInfo.getPorts()) { |
| if (presentCards.contains(portInfo.getIccId()) |
| || presentCards.contains(cardInfo.getEid())) { |
| slotIndex = cardInfo.getPhysicalSlotIndex(); |
| portIndex = portInfo.getPortIndex(); |
| Log.d(TAG, "SlotIndex : " + slotIndex + " and portIndex :" |
| + portIndex); |
| break; |
| } |
| } |
| } |
| if (slotIndex < 0) { |
| fail("Test must be run with SIM card inserted, presentCards = " |
| + presentCards + "cardinfos = " + cardInfos); |
| } |
| return Map.entry(slotIndex, portIndex); |
| }); |
| } |
| |
| public static void waitForMs(long ms) { |
| try { |
| Thread.sleep(ms); |
| } catch (InterruptedException e) { |
| Log.d(TAG, "InterruptedException while waiting: " + e); |
| } |
| } |
| |
| /** |
| * Verify that the phone is supporting the action of setForbiddenPlmn. |
| * |
| * @return whether to proceed the test |
| */ |
| private boolean supportSetFplmn() { |
| if (!hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)) { |
| return false; |
| } |
| return mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM; |
| } |
| |
| /** |
| * Verify that the phone is supporting the action of setForbiddenPlmn. |
| * |
| * @return whether to proceed the test |
| */ |
| private boolean test() { |
| if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { |
| return false; |
| } |
| return mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM; |
| } |
| |
| private static int makeRadioVersion(int major, int minor) { |
| if (major < 0 || minor < 0) return 0; |
| return major * 100 + minor; |
| } |
| |
| private Executor mSimpleExecutor = Runnable::run; |
| |
| private static MockSignalStrengthsTelephonyCallback mMockSignalStrengthsTelephonyCallback; |
| |
| private class MockSignalStrengthsTelephonyCallback extends TelephonyCallback |
| implements TelephonyCallback.SignalStrengthsListener { |
| @Override |
| public void onSignalStrengthsChanged(SignalStrength signalStrength) { |
| if (!mOnSignalStrengthsChanged) { |
| synchronized (mLock) { |
| mOnSignalStrengthsChanged = true; |
| mLock.notify(); |
| } |
| } |
| } |
| } |
| |
| @Test |
| public void testRegisterTelephonyCallbackWithNonLooper() throws Throwable { |
| mMockSignalStrengthsTelephonyCallback = new MockSignalStrengthsTelephonyCallback(); |
| |
| // Test register, generates an mOnSignalStrengthsChanged event |
| mTelephonyManager.registerTelephonyCallback(mSimpleExecutor, |
| mMockSignalStrengthsTelephonyCallback); |
| |
| synchronized (mLock) { |
| if (!mOnSignalStrengthsChanged) { |
| mLock.wait(TOLERANCE); |
| } |
| } |
| assertTrue("Test register, mOnSignalStrengthsChanged should be true.", |
| mOnSignalStrengthsChanged); |
| |
| // Test unregister |
| mOnSignalStrengthsChanged = false; |
| // unregister again, to make sure doing so does not call the listener |
| mTelephonyManager.unregisterTelephonyCallback(mMockSignalStrengthsTelephonyCallback); |
| |
| assertFalse("Test unregister, mOnSignalStrengthsChanged should be false.", |
| mOnSignalStrengthsChanged); |
| } |
| |
| private static MockCellInfoListener mMockCellInfoListener; |
| |
| private class MockCellInfoListener extends TelephonyCallback |
| implements TelephonyCallback.CellInfoListener { |
| @Override |
| public void onCellInfoChanged(@NonNull List<CellInfo> cellInfo) { |
| if (!mOnCellInfoChanged) { |
| synchronized (mLock) { |
| mOnCellInfoChanged = true; |
| mLock.notify(); |
| } |
| } |
| } |
| } |
| |
| @Test |
| public void testRegisterTelephonyCallback() throws Throwable { |
| if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { |
| // TODO: temp workaround, need to adjust test to for CDMA |
| return; |
| } |
| grantLocationPermissions(); |
| |
| TestThread t = new TestThread(() -> { |
| Looper.prepare(); |
| mMockCellInfoListener = new MockCellInfoListener(); |
| synchronized (mLock) { |
| mLock.notify(); // listener is ready |
| } |
| |
| Looper.loop(); |
| }); |
| |
| synchronized (mLock) { |
| t.start(); |
| mLock.wait(TOLERANCE); // wait for listener |
| } |
| |
| // Test register |
| synchronized (mLock) { |
| // .registerTelephonyCallback generates an onCellLocationChanged event |
| mTelephonyManager.registerTelephonyCallback(mSimpleExecutor, mMockCellInfoListener); |
| mLock.wait(TOLERANCE); |
| |
| assertTrue("Test register, mOnCellLocationChangedCalled should be true.", |
| mOnCellInfoChanged); |
| } |
| |
| synchronized (mLock) { |
| mOnCellInfoChanged = false; |
| |
| CellInfoResultsCallback resultsCallback = new CellInfoResultsCallback(); |
| mTelephonyManager.requestCellInfoUpdate(mSimpleExecutor, resultsCallback); |
| mLock.wait(TOLERANCE); |
| |
| assertTrue("Test register, mOnCellLocationChangedCalled should be true.", |
| mOnCellInfoChanged); |
| } |
| |
| // unregister the listener |
| mTelephonyManager.unregisterTelephonyCallback(mMockCellInfoListener); |
| Thread.sleep(TOLERANCE); |
| |
| // Test unregister |
| synchronized (mLock) { |
| mOnCellInfoChanged = false; |
| // unregister again, to make sure doing so does not call the listener |
| mTelephonyManager.unregisterTelephonyCallback(mMockCellInfoListener); |
| CellLocation.requestLocationUpdate(); |
| mLock.wait(TOLERANCE); |
| |
| assertFalse("Test unregister, mOnCellLocationChangedCalled should be false.", |
| mOnCellInfoChanged); |
| } |
| } |
| |
| private class CellInfoResultsCallback extends TelephonyManager.CellInfoCallback { |
| public List<CellInfo> cellInfo; |
| |
| @Override |
| public synchronized void onCellInfo(List<CellInfo> cellInfo) { |
| this.cellInfo = cellInfo; |
| notifyAll(); |
| } |
| |
| public synchronized void wait(int millis) throws InterruptedException { |
| if (cellInfo == null) { |
| super.wait(millis); |
| } |
| } |
| } |
| |
| private void setAppOpsPermissionAllowed(boolean allowed, String op) { |
| AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class); |
| int mode = allowed ? AppOpsManager.MODE_ALLOWED : AppOpsManager.opToDefaultMode(op); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| appOpsManager, (appOps) -> appOps.setUidMode(op, Process.myUid(), mode)); |
| } |
| |
| /** |
| * Verifies that {@link TelephonyManager#getNetworkSlicingConfiguration()} does not throw any |
| * exception |
| */ |
| @Test |
| public void testGetNetworkSlicingConfiguration() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| CompletableFuture<NetworkSlicingConfig> resultFuture = new CompletableFuture<>(); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.getNetworkSlicingConfiguration(mSimpleExecutor, resultFuture::complete)); |
| } |
| |
| @Test |
| @ApiTest(apis = {"android.telephony.TelephonyManager#checkCarrierPrivilegesForPackage"}) |
| public void testCheckCarrierPrivilegesForPackageEnforcesReadPrivilege() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| try { |
| InstrumentationRegistry.getInstrumentation() |
| .getUiAutomation() |
| .adoptShellPermissionIdentity( |
| android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE); |
| mTelephonyManager.checkCarrierPrivilegesForPackage(mSelfPackageName); |
| } catch (SecurityException e) { |
| fail("TelephonyManager#checkCarrierPrivilegesForPackage requires " |
| + "READ_PRIVILEGED_PHONE_STATE"); |
| } finally { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .dropShellPermissionIdentity(); |
| } |
| } |
| |
| @Test |
| public void testCheckCarrierPrivilegesForPackageThrowsExceptionWithoutReadPrivilege() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| try { |
| mTelephonyManager.checkCarrierPrivilegesForPackage(mSelfPackageName); |
| fail("TelephonyManager#checkCarrierPrivilegesForPackage must be protected " |
| + "with READ_PRIVILEGED_PHONE_STATE"); |
| } catch (SecurityException e) { |
| // expected |
| } |
| } |
| |
| @Test |
| @ApiTest(apis = {"android.telephony.TelephonyManager#checkCarrierPrivilegesForPackageAnyPhone"}) |
| public void testCheckCarrierPrivilegesForPackageAnyPhone() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| try { |
| mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(mSelfPackageName); |
| fail("TelephonyManager#checkCarrierPrivilegesForPackageAnyPhone must be protected " |
| + "with READ_PRIVILEGED_PHONE_STATE"); |
| } catch (SecurityException expected) { |
| } |
| |
| try { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .adoptShellPermissionIdentity( |
| android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE); |
| mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(mSelfPackageName); |
| } catch (SecurityException e) { |
| fail("TelephonyManager#checkCarrierPrivilegesForPackageAnyPhone should not throw " |
| + "SecurityException with READ_PRIVILEGED_PHONE_STATE permission"); |
| } finally { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .dropShellPermissionIdentity(); |
| } |
| } |
| |
| @Test |
| @ApiTest(apis = {"android.telephony.TelephonyManager#getCarrierPackageNamesForIntentAndPhone"}) |
| public void testGetCarrierPackageNamesForIntentAndPhoneEnforcesReadPrivilege() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| try { |
| InstrumentationRegistry.getInstrumentation() |
| .getUiAutomation() |
| .adoptShellPermissionIdentity( |
| android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE); |
| Intent intent = new Intent(); |
| int phoneId = 1; |
| mTelephonyManager.getCarrierPackageNamesForIntentAndPhone(intent, phoneId); |
| } catch (SecurityException e) { |
| fail("TelephonyManager#getCarrierPackageNamesForIntentAndPhone requires " |
| + "READ_PRIVILEGED_PHONE_STATE"); |
| } finally { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .dropShellPermissionIdentity(); |
| } |
| } |
| |
| @Test |
| public void testGetCarrierPackageNamesForIntentAndPhoneThrowsExceptionWithoutReadPrivilege() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| try { |
| Intent intent = new Intent(); |
| int phoneId = 1; |
| mTelephonyManager.getCarrierPackageNamesForIntentAndPhone(intent, phoneId); |
| fail("TelephonyManager#getCarrierPackageNamesForIntentAndPhone must be protected " |
| + "with READ_PRIVILEGED_PHONE_STATE"); |
| } catch (SecurityException e) { |
| // expected |
| } finally { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .dropShellPermissionIdentity(); |
| } |
| } |
| |
| @Test |
| @ApiTest(apis = {"android.telephony.TelephonyManager#getPackagesWithCarrierPrivileges"}) |
| public void testGetPackagesWithCarrierPrivilegesEnforcesReadPrivilege() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| try { |
| InstrumentationRegistry.getInstrumentation() |
| .getUiAutomation() |
| .adoptShellPermissionIdentity( |
| android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE); |
| mTelephonyManager.getPackagesWithCarrierPrivileges(); |
| } catch (SecurityException e) { |
| fail("TelephonyManager#getPackagesWithCarrierPrivileges requires " |
| + "READ_PRIVILEGED_PHONE_STATE"); |
| } finally { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .dropShellPermissionIdentity(); |
| } |
| } |
| |
| @Test |
| public void testGetPackagesWithCarrierPrivilegesThrowsExceptionWithoutReadPrivilege() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| try { |
| mTelephonyManager.getPackagesWithCarrierPrivileges(); |
| fail("TelephonyManager#getPackagesWithCarrierPrivileges must be protected " |
| + "with READ_PRIVILEGED_PHONE_STATE"); |
| } catch (SecurityException e) { |
| // expected |
| } |
| } |
| |
| @Test |
| @ApiTest(apis = {"android.telephony.TelephonyManager#getSimSlotMapping", |
| "android.telephony.TelephonyManager#setSimSlotMapping"}) |
| public void testSimSlotMapping() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| InstrumentationRegistry.getInstrumentation() |
| .getUiAutomation() |
| .adoptShellPermissionIdentity( |
| android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE); |
| Collection<UiccSlotMapping> simSlotMapping = mTelephonyManager.getSimSlotMapping(); |
| // passing slotMapping combination |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .adoptShellPermissionIdentity(android.Manifest.permission.MODIFY_PHONE_STATE); |
| try { |
| mTelephonyManager.setSimSlotMapping(simSlotMapping); |
| } catch (IllegalArgumentException | IllegalStateException e) { |
| // if HAL version is less than 2.0, vendors may not have implemented API, |
| // skipping the failure. |
| if (mConfigHalVersion >= RADIO_HAL_VERSION_2_0) { |
| fail("Not Expected Fail, Error in setSimSlotMapping :" + e); |
| } |
| } |
| |
| List<UiccSlotMapping> slotMappingList = new ArrayList<>(); |
| // invalid logicalSlotIndex - Fail |
| UiccSlotMapping slotMapping1 = new UiccSlotMapping( |
| TelephonyManager.DEFAULT_PORT_INDEX, /*portIndex*/ |
| 1, /*physicalSlotIndex*/ |
| SubscriptionManager.INVALID_PHONE_INDEX /*logicalSlotIndex*/); |
| UiccSlotMapping slotMapping2 = new UiccSlotMapping( |
| TelephonyManager.DEFAULT_PORT_INDEX, /*portIndex*/ |
| 0, /*physicalSlotIndex*/ |
| 0 /*logicalSlotIndex*/); |
| slotMappingList.add(slotMapping1); |
| slotMappingList.add(slotMapping2); |
| try { |
| mTelephonyManager.setSimSlotMapping(slotMappingList); |
| fail("Expected IllegalStateException, invalid UiccSlotMapping data found"); |
| } catch (IllegalStateException e) { |
| //expected |
| } |
| slotMappingList.clear(); |
| |
| // Duplicate logicalSlotIndex - Fail |
| UiccSlotMapping slotMapping3 = new UiccSlotMapping( |
| TelephonyManager.DEFAULT_PORT_INDEX, /*portIndex*/ |
| 1, /*physicalSlotIndex*/ |
| 0 /*logicalSlotIndex*/); |
| UiccSlotMapping slotMapping4 = new UiccSlotMapping( |
| TelephonyManager.DEFAULT_PORT_INDEX, /*portIndex*/ |
| 0, /*physicalSlotIndex*/ |
| 0 /*logicalSlotIndex*/); |
| slotMappingList.add(slotMapping3); |
| slotMappingList.add(slotMapping4); |
| try { |
| mTelephonyManager.setSimSlotMapping(slotMappingList); |
| fail("Expected IllegalArgumentException, Duplicate UiccSlotMapping data found"); |
| } catch (IllegalArgumentException e) { |
| //expected |
| } |
| slotMappingList.clear(); |
| |
| // Duplicate {portIndex+physicalSlotIndex} - Fail |
| UiccSlotMapping slotMapping5 = new UiccSlotMapping( |
| TelephonyManager.DEFAULT_PORT_INDEX, /*portIndex*/ |
| 1, /*physicalSlotIndex*/ |
| 0 /*logicalSlotIndex*/); |
| UiccSlotMapping slotMapping6 = new UiccSlotMapping( |
| TelephonyManager.DEFAULT_PORT_INDEX, /*portIndex*/ |
| 1, /*physicalSlotIndex*/ |
| 1 /*logicalSlotIndex*/); |
| slotMappingList.add(slotMapping5); |
| slotMappingList.add(slotMapping6); |
| try { |
| mTelephonyManager.setSimSlotMapping(slotMappingList); |
| fail("Expected IllegalArgumentException, Duplicate UiccSlotMapping data found"); |
| } catch (IllegalArgumentException e) { |
| //expected |
| } |
| slotMappingList.clear(); |
| |
| // Duplicate {portIndex+physicalSlotIndex+logicalSlotIndex} - Fail |
| UiccSlotMapping slotMapping7 = new UiccSlotMapping( |
| TelephonyManager.DEFAULT_PORT_INDEX, /*portIndex*/ |
| 1, /*physicalSlotIndex*/ |
| 0 /*logicalSlotIndex*/); |
| UiccSlotMapping slotMapping8 = new UiccSlotMapping( |
| TelephonyManager.DEFAULT_PORT_INDEX, /*portIndex*/ |
| 1, /*physicalSlotIndex*/ |
| 0 /*logicalSlotIndex*/); |
| slotMappingList.add(slotMapping7); |
| slotMappingList.add(slotMapping8); |
| try { |
| mTelephonyManager.setSimSlotMapping(slotMappingList); |
| fail("Expected IllegalArgumentException, Duplicate UiccSlotMapping data found"); |
| } catch (IllegalArgumentException e) { |
| //expected |
| } |
| slotMappingList.clear(); |
| |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .dropShellPermissionIdentity(); |
| |
| } |
| |
| @Test |
| @ApiTest(apis = {"android.telephony.TelephonyManager#getUiccSlotsInfo"}) |
| public void getUiccSlotInfoTest() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| InstrumentationRegistry.getInstrumentation() |
| .getUiAutomation() |
| .adoptShellPermissionIdentity( |
| android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE); |
| UiccSlotInfo[] slotInfos = mTelephonyManager.getUiccSlotsInfo(); |
| |
| if (slotInfos == null) { |
| return; |
| } |
| |
| // Call below methods to make sure it doesn't crash. |
| for (UiccSlotInfo slotInfo : slotInfos) { |
| slotInfo.getIsEuicc(); |
| slotInfo.getCardId(); |
| slotInfo.getCardStateInfo(); |
| slotInfo.getIsExtendedApduSupported(); |
| slotInfo.isRemovable(); |
| for (UiccPortInfo portInfo :slotInfo.getPorts()) { |
| portInfo.isActive(); |
| portInfo.getIccId(); |
| portInfo.getLogicalSlotIndex(); |
| portInfo.getPortIndex(); |
| } |
| } |
| |
| for (UiccSlotInfo slotInfo : slotInfos) { |
| // Make sure portIndex value is less than the number of ports available. |
| int count = slotInfo.getPorts().stream().filter(portInfo |
| -> portInfo.getPortIndex() >= slotInfo.getPorts().size()).toList().size(); |
| if (count > 0) { |
| fail("port index should be less than the total number of ports available"); |
| } |
| // Make sure both port indexes are unique. |
| for (int index = 0; index < slotInfo.getPorts().size(); index++) { |
| final int portIndex = index; |
| assertEquals(1, slotInfo.getPorts().stream().filter( |
| portInfo -> portInfo.getPortIndex() == portIndex).toList().size()); |
| } |
| } |
| |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .dropShellPermissionIdentity(); |
| } |
| |
| @Test |
| public void testGetUiccSlotInfosFailsWithoutReadPhoneStatePrivilege() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| try { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .dropShellPermissionIdentity(); |
| mTelephonyManager.getUiccSlotsInfo(); |
| fail("TelephonyManager#getUiccSlotsInfo must be protected " |
| + "with READ_PRIVILEGED_PHONE_STATE"); |
| } catch (SecurityException e) { |
| // expected |
| } |
| } |
| |
| @Test |
| public void getSimSlotMappingTestReadPermission() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| try { |
| Collection<UiccSlotMapping> simSlotMapping = mTelephonyManager.getSimSlotMapping(); |
| fail("Expected SecurityException, no READ_PRIVILEGED_PHONE_STATE permission"); |
| } catch (SecurityException e) { |
| // expected |
| } |
| } |
| |
| @Test |
| public void testSetAllowedNetworkTypesForReason_ignoreInvalidNetworkType() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| // NETWORK_TYPE_BITMASK_LTE_CA is invalid, should be converted into NETWORK_TYPE_BITMASK_LTE |
| long invalidAllowedNetworkTypes = TelephonyManager.NETWORK_TYPE_BITMASK_LTE |
| | TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA; |
| long expectedAllowedNetworkTypes = TelephonyManager.NETWORK_TYPE_BITMASK_LTE; |
| try { |
| mIsAllowedNetworkTypeChanged = true; |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( |
| mTelephonyManager, |
| (tm) -> tm.setAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER, |
| invalidAllowedNetworkTypes)); |
| |
| long deviceAllowedNetworkTypes = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> { |
| return tm.getAllowedNetworkTypesForReason( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER); |
| } |
| ); |
| assertEquals(expectedAllowedNetworkTypes, deviceAllowedNetworkTypes); |
| } catch (SecurityException se) { |
| fail("testIgnoreInvalidNetworkType: SecurityException not expected"); |
| } |
| } |
| |
| @Test |
| @ApiTest(apis = {"android.telephony.TelephonyManager#getSimSlotMapping"}) |
| public void getSimSlotMappingTest() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| |
| InstrumentationRegistry.getInstrumentation() |
| .getUiAutomation() |
| .adoptShellPermissionIdentity( |
| android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE); |
| try { |
| Collection<UiccSlotMapping> simSlotMapping = mTelephonyManager.getSimSlotMapping(); |
| assertTrue(isSlotMappingValid(simSlotMapping)); |
| } catch (IllegalArgumentException e) { |
| fail("IllegalArgumentException, Duplicate UiccSlotMapping data found"); |
| } finally { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .dropShellPermissionIdentity(); |
| } |
| } |
| private static boolean isSlotMappingValid(@NonNull Collection<UiccSlotMapping> slotMapping) { |
| // Grouping the collection by logicalSlotIndex, finding different entries mapping to the |
| // same logical slot |
| Map<Integer, List<UiccSlotMapping>> slotMappingInfo = slotMapping.stream().collect( |
| Collectors.groupingBy(UiccSlotMapping::getLogicalSlotIndex)); |
| for (Map.Entry<Integer, List<UiccSlotMapping>> entry : slotMappingInfo.entrySet()) { |
| List<UiccSlotMapping> logicalSlotMap = entry.getValue(); |
| if (logicalSlotMap.size() > 1) { |
| // duplicate logicalSlotIndex found |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public static class ServiceStateRadioStateListener extends TelephonyCallback |
| implements TelephonyCallback.ServiceStateListener, |
| TelephonyCallback.RadioPowerStateListener { |
| private static final long TIMEOUT_TO_WAIT_FOR_DESIRED_STATE = |
| TimeUnit.SECONDS.toMillis(20); |
| private final Object mPowerStateLock = new Object(); |
| private final Object mServiceStateLock = new Object(); |
| ServiceState mServiceState; |
| int mDesireServiceState; |
| |
| int mRadioPowerState; |
| int mDesireRadioPowerState; |
| |
| public ServiceStateRadioStateListener(ServiceState serviceState, int radioPowerState) { |
| mServiceState = serviceState; |
| mRadioPowerState = radioPowerState; |
| mDesireRadioPowerState = radioPowerState; |
| } |
| |
| @Override |
| public void onServiceStateChanged(ServiceState ss) { |
| Log.d(TAG, "onServiceStateChanged to " + ss); |
| synchronized (mServiceStateLock) { |
| mServiceState = ss; |
| if (ss.getState() == mDesireServiceState) { |
| mServiceStateLock.notify(); |
| } |
| } |
| } |
| |
| @Override |
| public void onRadioPowerStateChanged(int radioState) { |
| Log.d(TAG, "onRadioPowerStateChanged to " + radioState); |
| synchronized (mPowerStateLock) { |
| mRadioPowerState = radioState; |
| if (radioState == mDesireRadioPowerState) { |
| mPowerStateLock.notify(); |
| } |
| } |
| } |
| |
| public void waitForRadioStateIntent(int desiredRadioState) { |
| Log.d(TAG, "waitForRadioStateIntent: desiredRadioState=" + desiredRadioState); |
| synchronized (mPowerStateLock) { |
| mDesireRadioPowerState = desiredRadioState; |
| /** |
| * Since SST sets waiting time up to 10 seconds for the power off radio, the |
| * RadioStateIntent timer extends the wait time up to 20 seconds here as well. |
| */ |
| waitForDesiredState(mPowerStateLock, desiredRadioState, |
| () -> mRadioPowerState, true); |
| } |
| } |
| |
| public void waitForServiceStateIntent(int desiredServiceState, boolean failOnTimeOut) { |
| Log.d(TAG, "waitForServiceStateIntent: desiredServiceState=" + desiredServiceState); |
| synchronized (mServiceStateLock) { |
| mDesireServiceState = desiredServiceState; |
| waitForDesiredState(mServiceStateLock, desiredServiceState, |
| () -> mServiceState.getState(), failOnTimeOut); |
| } |
| } |
| |
| private void waitForDesiredState(@NonNull Object lock, int desiredState, |
| @NonNull IntSupplier currentStateSupplier, boolean failOnTimeOut) { |
| synchronized (lock) { |
| long now = SystemClock.elapsedRealtime(); |
| long deadline = now + TIMEOUT_TO_WAIT_FOR_DESIRED_STATE; |
| while (currentStateSupplier.getAsInt() != desiredState && now < deadline) { |
| try { |
| lock.wait(TIMEOUT_TO_WAIT_FOR_DESIRED_STATE); |
| } catch (Exception e) { |
| if (failOnTimeOut) { |
| fail(e.getMessage()); |
| } else { |
| Log.w(TAG, "waitForDesiredState: e=" + e); |
| } |
| } |
| now = SystemClock.elapsedRealtime(); |
| } |
| } |
| } |
| } |
| |
| @Test |
| public void testSetVoiceServiceStateOverride() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_CALLING)); |
| ServiceStateRadioStateListener callback = new ServiceStateRadioStateListener( |
| mTelephonyManager.getServiceState(), mTelephonyManager.getRadioPowerState()); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.registerTelephonyCallback(Runnable::run, callback)); |
| |
| boolean turnedRadioOff = false; |
| boolean setServiceStateOverride = false; |
| try { |
| if (mTelephonyManager.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) { |
| Log.i(TAG, "testSetVoiceServiceStateOverride: turning radio off to force OOS"); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setRadioPower(false), permission.MODIFY_PHONE_STATE); |
| callback.waitForRadioStateIntent(TelephonyManager.RADIO_POWER_OFF); |
| callback.waitForServiceStateIntent(ServiceState.STATE_POWER_OFF, true); |
| assertEquals(TelephonyManager.RADIO_POWER_OFF, callback.mRadioPowerState); |
| assertEquals(ServiceState.STATE_POWER_OFF, callback.mServiceState.getState()); |
| turnedRadioOff = true; |
| } |
| // This could be OUT_OF_SERVICE or POWER_OFF, it doesn't really matter for this test as |
| // long as it's not IN_SERVICE |
| ServiceState serviceState = mTelephonyManager.getServiceState(); |
| int retry = 0; |
| while ((serviceState == null |
| || serviceState.getState() == ServiceState.STATE_IN_SERVICE) && retry < 3) { |
| serviceState = mTelephonyManager.getServiceState(); |
| retry++; |
| // wait up to 3s for radio power off/out of service |
| waitForMs(1000); |
| } |
| int originalServiceState = serviceState != null ? serviceState.getState() |
| : callback.mServiceState.getState(); |
| Log.i(TAG, "testSetVoiceServiceStateOverride: originalSS = " + originalServiceState); |
| assertNotEquals(ServiceState.STATE_IN_SERVICE, originalServiceState); |
| |
| // Telecom will sometimes remove the override after radio reboots. |
| // Retry setting the override to prevent flaky test failures. |
| int listenerState = callback.mServiceState.getState(); |
| int telephonyManagerState = originalServiceState; |
| retry = 0; |
| while ((listenerState != ServiceState.STATE_IN_SERVICE |
| || telephonyManagerState != ServiceState.STATE_IN_SERVICE) && retry < 3) { |
| // We should see the override in both ServiceStateListener and getServiceState |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setVoiceServiceStateOverride(true), |
| permission.BIND_TELECOM_CONNECTION_SERVICE); |
| callback.waitForServiceStateIntent(ServiceState.STATE_IN_SERVICE, false); |
| setServiceStateOverride = true; |
| |
| serviceState = mTelephonyManager.getServiceState(); |
| if (serviceState != null) { |
| telephonyManagerState = serviceState.getState(); |
| } |
| listenerState = callback.mServiceState.getState(); |
| retry++; |
| } |
| assertEquals(ServiceState.STATE_IN_SERVICE, listenerState); |
| assertEquals(ServiceState.STATE_IN_SERVICE, telephonyManagerState); |
| |
| // When we take away the override, things flip back to the original state since there |
| // were no other material changes made to the device that would impact ServiceState |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setVoiceServiceStateOverride(false), |
| permission.BIND_TELECOM_CONNECTION_SERVICE); |
| callback.waitForServiceStateIntent(originalServiceState, true); |
| assertEquals(originalServiceState, callback.mServiceState.getState()); |
| assertEquals(originalServiceState, mTelephonyManager.getServiceState().getState()); |
| } finally { |
| if (setServiceStateOverride) { |
| // No harm in calling this again if we already did, but call just in case we failed |
| // an assertion related to setOverride(true) |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setVoiceServiceStateOverride(false), |
| permission.BIND_TELECOM_CONNECTION_SERVICE); |
| } |
| if (turnedRadioOff) { |
| // Turn the radio back on and wait for ServiceState to become stable again, so we |
| // don't cause flakes in other tests |
| Log.i(TAG, "testSetVoiceServiceStateOverride: turning radio back on"); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setRadioPower(true), permission.MODIFY_PHONE_STATE); |
| callback.waitForRadioStateIntent(TelephonyManager.RADIO_POWER_ON); |
| callback.waitForServiceStateIntent(ServiceState.STATE_IN_SERVICE, true); |
| assertEquals(TelephonyManager.RADIO_POWER_ON, callback.mRadioPowerState); |
| assertEquals(ServiceState.STATE_IN_SERVICE, callback.mServiceState.getState()); |
| } |
| } |
| } |
| |
| @Test |
| @ApiTest(apis = { |
| "android.telephony.TelephonyManager#requestRadioPowerOffForReason", |
| "android.telephony.TelephonyManager#clearRadioPowerOffForReason", |
| "android.telephony.TelephonyManager#getRadioPowerOffReasons"}) |
| public void testSetRadioPowerForReasonNearbyDevice() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| ServiceStateRadioStateListener callback = new ServiceStateRadioStateListener( |
| mTelephonyManager.getServiceState(), mTelephonyManager.getRadioPowerState()); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.registerTelephonyCallback(Runnable::run, callback)); |
| |
| boolean turnedRadioOn = false; |
| if (mTelephonyManager.getRadioPowerState() == TelephonyManager.RADIO_POWER_OFF) { |
| Log.i(TAG, "testSetRadioPowerForReasonNearbyDevice:" |
| + "turning on radio since it is off"); |
| turnRadioOn(callback, TelephonyManager.RADIO_POWER_REASON_USER); |
| assertEquals(TelephonyManager.RADIO_POWER_ON, callback.mRadioPowerState); |
| turnedRadioOn = true; |
| } |
| |
| Log.i(TAG, "testSetRadioPowerForReasonNearbyDevice:" |
| + "turning radio off due to nearby device ..."); |
| turnRadioOff(callback, TelephonyManager.RADIO_POWER_REASON_NEARBY_DEVICE); |
| assertRadioOffWithReason(callback, TelephonyManager.RADIO_POWER_REASON_NEARBY_DEVICE); |
| |
| Log.i(TAG, "testSetRadioPowerForReasonNearbyDevice: turning on airplane mode ..."); |
| turnRadioOff(callback, TelephonyManager.RADIO_POWER_REASON_USER); |
| assertRadioOffWithReason(callback, TelephonyManager.RADIO_POWER_REASON_USER); |
| |
| Log.i(TAG, "testSetRadioPowerForReasonNearbyDevice: turning off airplane mode ..."); |
| turnRadioOn(callback, TelephonyManager.RADIO_POWER_REASON_USER); |
| assertRadioOffWithReason(callback, TelephonyManager.RADIO_POWER_REASON_NEARBY_DEVICE); |
| |
| Log.i(TAG, "testSetRadioPowerForReasonNearbyDevice:" |
| + " turning on radio due to nearby device..."); |
| turnRadioOn(callback, TelephonyManager.RADIO_POWER_REASON_NEARBY_DEVICE); |
| assertEquals(TelephonyManager.RADIO_POWER_ON, callback.mRadioPowerState); |
| |
| if (turnedRadioOn) { |
| Log.i(TAG, "testSetRadioPowerForReasonNearbyDevice: turning radio back off"); |
| turnRadioOff(callback, TelephonyManager.RADIO_POWER_REASON_USER); |
| assertRadioOffWithReason(callback, TelephonyManager.RADIO_POWER_REASON_USER); |
| } |
| } |
| |
| @Test |
| @ApiTest(apis = { |
| "android.telephony.TelephonyManager#requestRadioPowerOffForReason", |
| "android.telephony.TelephonyManager#clearRadioPowerOffForReason", |
| "android.telephony.TelephonyManager#getRadioPowerOffReasons", |
| "android.telephony.TelephonyManager#setRadioEnabled"}) |
| public void testSetRadioPowerForReasonCarrier() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| ServiceStateRadioStateListener callback = new ServiceStateRadioStateListener( |
| mTelephonyManager.getServiceState(), mTelephonyManager.getRadioPowerState()); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.registerTelephonyCallback(Runnable::run, callback)); |
| |
| boolean turnedRadioOn = false; |
| if (mTelephonyManager.getRadioPowerState() == TelephonyManager.RADIO_POWER_OFF) { |
| Log.i(TAG, "testSetRadioPowerForReasonCarrier: turning on radio since it is off"); |
| turnRadioOn(callback, TelephonyManager.RADIO_POWER_REASON_USER); |
| assertEquals(TelephonyManager.RADIO_POWER_ON, callback.mRadioPowerState); |
| turnedRadioOn = true; |
| } |
| |
| Log.i(TAG, "testSetRadioPowerForReasonCarrier: turning radio off due to carrier ..."); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setRadioEnabled(false), permission.MODIFY_PHONE_STATE); |
| callback.waitForRadioStateIntent(TelephonyManager.RADIO_POWER_OFF); |
| assertRadioOffWithReason(callback, TelephonyManager.RADIO_POWER_REASON_CARRIER); |
| |
| Log.i(TAG, "testSetRadioPowerForReasonCarrier: turning on airplane mode ..."); |
| turnRadioOff(callback, TelephonyManager.RADIO_POWER_REASON_USER); |
| assertRadioOffWithReason(callback, TelephonyManager.RADIO_POWER_REASON_CARRIER); |
| |
| Log.i(TAG, "testSetRadioPowerForReasonCarrier: turning off airplane mode ..."); |
| turnRadioOn(callback, TelephonyManager.RADIO_POWER_REASON_USER); |
| assertRadioOffWithReason(callback, TelephonyManager.RADIO_POWER_REASON_CARRIER); |
| |
| Log.i(TAG, "testSetRadioPowerForReasonCarrier: turning on radio due to carrier..."); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.setRadioEnabled(true), permission.MODIFY_PHONE_STATE); |
| callback.waitForRadioStateIntent(TelephonyManager.RADIO_POWER_ON); |
| assertEquals(TelephonyManager.RADIO_POWER_ON, callback.mRadioPowerState); |
| |
| if (turnedRadioOn) { |
| Log.i(TAG, "testSetRadioPowerForReasonCarrier: turning radio back off"); |
| turnRadioOff(callback, TelephonyManager.RADIO_POWER_REASON_USER); |
| assertRadioOffWithReason(callback, TelephonyManager.RADIO_POWER_REASON_USER); |
| } |
| } |
| |
| @Test |
| @ApiTest(apis = { |
| "android.telephony.TelephonyManager#getCellBroadcastIdRanges", |
| "android.telephony.TelephonyManager#setCellBroadcastIdRanges"}) |
| public void testSetCellBroadcastIdRanges() throws Exception { |
| final List<CellBroadcastIdRange> ranges = new ArrayList<>(); |
| ranges.add(new CellBroadcastIdRange(0, 999, SmsCbMessage.MESSAGE_FORMAT_3GPP, true)); |
| |
| // Permission check |
| assertThrows(SecurityException.class, () -> |
| mTelephonyManager.getCellBroadcastIdRanges()); |
| |
| assertThrows(SecurityException.class, () -> |
| mTelephonyManager.setCellBroadcastIdRanges(ranges, |
| AsyncTask.SERIAL_EXECUTOR, (result) -> {})); |
| |
| final List<Integer> resultsExpected = new ArrayList<>(); |
| resultsExpected.add(TelephonyManager.CELL_BROADCAST_RESULT_UNKNOWN); |
| resultsExpected.add(TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS); |
| resultsExpected.add(TelephonyManager.CELL_BROADCAST_RESULT_UNSUPPORTED); |
| resultsExpected.add(TelephonyManager.CELL_BROADCAST_RESULT_FAIL_CONFIG); |
| resultsExpected.add(TelephonyManager.CELL_BROADCAST_RESULT_FAIL_ACTIVATION); |
| |
| final List<CellBroadcastIdRange> rangesExpected = ShellIdentityUtils |
| .invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.getCellBroadcastIdRanges()); |
| CountDownLatch latch = new CountDownLatch(1); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setCellBroadcastIdRanges(ranges, AsyncTask.SERIAL_EXECUTOR, |
| (result) -> { |
| latch.countDown(); |
| // The result must be a valid value |
| assertTrue("Got " + result + " not in expected set", |
| resultsExpected.contains(result)); |
| // The range will be updated when result is success |
| if (result == TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS) { |
| rangesExpected.clear(); |
| rangesExpected.addAll(ranges); |
| } |
| })); |
| assertTrue(latch.await(500, TimeUnit.MILLISECONDS)); |
| List<CellBroadcastIdRange> ranges2 = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.getCellBroadcastIdRanges()); |
| |
| assertEquals(rangesExpected, ranges2); |
| |
| ranges.add(new CellBroadcastIdRange(999, 999, SmsCbMessage.MESSAGE_FORMAT_3GPP, false)); |
| assertThrows(IllegalArgumentException.class, () -> |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.setCellBroadcastIdRanges(ranges, null, null))); |
| } |
| |
| private void turnRadioOn(ServiceStateRadioStateListener callback, int reason) { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.clearRadioPowerOffForReason(reason), permission.MODIFY_PHONE_STATE); |
| callback.waitForRadioStateIntent(TelephonyManager.RADIO_POWER_ON); |
| } |
| |
| private void turnRadioOff(ServiceStateRadioStateListener callback, int reason) { |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.requestRadioPowerOffForReason(reason), permission.MODIFY_PHONE_STATE); |
| callback.waitForRadioStateIntent(TelephonyManager.RADIO_POWER_OFF); |
| } |
| |
| private void assertRadioOffWithReason(ServiceStateRadioStateListener callback, int reason) { |
| assertEquals(TelephonyManager.RADIO_POWER_OFF, callback.mRadioPowerState); |
| |
| Set<Integer> radioPowerOffReasons = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, |
| tm -> tm.getRadioPowerOffReasons(), permission.READ_PRIVILEGED_PHONE_STATE); |
| assertTrue(radioPowerOffReasons.contains(reason)); |
| } |
| |
| private void assertRadioOffWithReason(TelephonyManager telephonyManager, |
| ServiceStateRadioStateListener callback, int reason) { |
| assertEquals(TelephonyManager.RADIO_POWER_OFF, callback.mRadioPowerState); |
| |
| Set<Integer> radioPowerOffReasons = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| telephonyManager, |
| tm -> tm.getRadioPowerOffReasons(), permission.READ_PRIVILEGED_PHONE_STATE); |
| assertTrue(radioPowerOffReasons.contains(reason)); |
| } |
| |
| /** |
| * Verifies that {@link TelephonyManager#getImsPrivateUserIdentity()} does not throw any |
| * exception when called and has the correct permissions. |
| */ |
| @Ignore("TelephonyManager#getImsPrivateUserIdentity()" + " is hidden. Internal use only.") |
| @Test |
| public void getImsPrivateUserIdentity() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| // make sure not to face any permission problem while calling the API |
| try { |
| setAppOpsPermissionAllowed(true, OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER); |
| mTelephonyManager.getImsPrivateUserIdentity(); |
| } catch (IllegalStateException e) { |
| // expected in case SIM do not support ISIM |
| } catch (SecurityException secExp) { |
| fail(); |
| } finally { |
| setAppOpsPermissionAllowed(false, OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER); |
| } |
| } |
| |
| /** |
| * Verifies that {@link TelephonyManager#getImsPrivateUserIdentity()} does throw |
| * SecurityException when required permissions are not granted. |
| */ |
| @Ignore("TelephonyManager#getImsPrivateUserIdentity()" + " is hidden. Internal use only.") |
| @Test |
| public void getImsPrivateUserIdentity_NoPermissionGranted() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| try { |
| mTelephonyManager.getImsPrivateUserIdentity(); |
| fail(); // if no SecurityException then it fails() |
| } catch (IllegalStateException e) { |
| // expected in case SIM do not support ISIM |
| } catch (SecurityException secExp) { |
| // expected as API has no permission to fetch ISIM |
| } |
| } |
| |
| /** |
| * Verifies that {@link TelephonyManager#getImsPublicUserIdentities()} does not throw any |
| * exception when granted with READ_PRIVILEGED_PHONE_STATE permission. |
| */ |
| @Ignore("TelephonyManager#getImsPublicUserIdentities()" + " is hidden. Internal use only.") |
| @Test |
| public void getImsPublicUserIdentities_ReadPrivilegedPermission() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| // make sure not to face any permission problem while calling the API |
| try { |
| List<Uri> impuList = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, tm -> tm.getImsPublicUserIdentities(), |
| Manifest.permission.READ_PRIVILEGED_PHONE_STATE); |
| assertNotNull(impuList); |
| for (Uri impu : impuList) { |
| assertTrue(impu.getScheme().equalsIgnoreCase("sip")); |
| } |
| } catch (IllegalStateException e) { |
| // expected in case SIM do not support ISIM |
| fail(); |
| } |
| } |
| |
| /** |
| * Verifies that {@link TelephonyManager#getImsPublicUserIdentities()} does not throw any |
| * exception when granted with READ_PHONE_NUMBERS permission. |
| */ |
| @Ignore("TelephonyManager#getImsPublicUserIdentities()" + " is hidden. Internal use only.") |
| @Test |
| public void getImsPublicUserIdentities_ReadPhoneNumberPermission() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| // make sure not to face any permission problem while calling the API |
| try { |
| List<Uri> impuList = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, tm -> tm.getImsPublicUserIdentities(), |
| Manifest.permission.READ_PHONE_NUMBERS); |
| assertNotNull(impuList); |
| for (Uri impu : impuList) { |
| assertTrue(impu.getScheme().equalsIgnoreCase("sip")); |
| } |
| } catch (IllegalStateException e) { |
| // expected in case SIM do not support ISIM |
| } |
| } |
| |
| /** |
| * Verifies that {@link TelephonyManager#getImsPublicUserIdentities()} does throw |
| * SecurityException when called with out any permissions granted. |
| */ |
| @Ignore("TelephonyManager#getImsPublicUserIdentities()" + " is hidden. Internal use only.") |
| @Test |
| public void getImsPublicUserIdentities_NoPermissionGranted() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); |
| try { |
| if (hasReadContactsPermission(mSelfPackageName)) { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation(). |
| revokeRuntimePermission(mSelfPackageName, |
| "android.permission.READ_PHONE_NUMBERS"); |
| } |
| List<Uri> impuList = mTelephonyManager.getImsPublicUserIdentities(); |
| fail(); // if no SecurityException then it fails() |
| } catch (IllegalStateException e) { |
| // expected in case SIM do not support ISIM |
| } catch (SecurityException secExp) { |
| // expected as caller is not granted with required permissions |
| } |
| } |
| |
| private boolean hasReadContactsPermission(String pkgName) { |
| return mPackageManager.checkPermission(Manifest.permission.READ_CONTACTS, pkgName) |
| == PackageManager.PERMISSION_GRANTED; |
| } |
| |
| @Test |
| public void testLastKnownCountryIso() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| |
| CountryChangedReceiver countryChangedReceiver = new CountryChangedReceiver(); |
| |
| getContext().registerReceiver(countryChangedReceiver, |
| new IntentFilter(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED), |
| Context.RECEIVER_EXPORTED); |
| |
| ServiceStateRadioStateListener callback = new ServiceStateRadioStateListener( |
| mTelephonyManager.getServiceState(), mTelephonyManager.getRadioPowerState()); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.registerTelephonyCallback(Runnable::run, callback)); |
| |
| int initialRadioState = mTelephonyManager.getRadioPowerState(); |
| try { |
| if (initialRadioState == TelephonyManager.RADIO_POWER_OFF) { |
| Log.i(TAG, "testLastKnownCountryIso:" |
| + "turning on radio since it is off"); |
| turnRadioOn(callback, TelephonyManager.RADIO_POWER_REASON_USER); |
| assertEquals(TelephonyManager.RADIO_POWER_ON, callback.mRadioPowerState); |
| } |
| |
| String countryCode = mTelephonyManager.getNetworkCountryIso(); |
| if (TextUtils.isEmpty(countryCode)) { |
| Log.i(TAG, "testLastKnownCountryIso: country iso is already known. Not testable."); |
| // Not testable. |
| return; |
| } |
| |
| Log.i(TAG, "testLastKnownCountryIso:" |
| + "turning radio off due to testing last known country ..."); |
| turnRadioOff(callback, TelephonyManager.RADIO_POWER_REASON_USER); |
| try { |
| countryChangedReceiver.waitForIntent(); |
| assertThat(countryChangedReceiver.getExtras().getString( |
| TelephonyManager.EXTRA_NETWORK_COUNTRY)).isEmpty(); |
| assertThat(countryChangedReceiver.getExtras().getString( |
| TelephonyManager.EXTRA_LAST_KNOWN_NETWORK_COUNTRY)).isEqualTo(countryCode); |
| Log.i(TAG, "testLastKnownCountryIso: country code \"" + countryCode |
| + "\" matched."); |
| } catch (Exception e) { |
| fail(e.getMessage()); |
| } |
| } finally { |
| if (initialRadioState == TelephonyManager.RADIO_POWER_OFF |
| && mTelephonyManager.getRadioPowerState() != TelephonyManager.RADIO_POWER_OFF) { |
| Log.i(TAG, "testLastKnownCountryIso: turning radio back off"); |
| turnRadioOff(callback, TelephonyManager.RADIO_POWER_REASON_USER); |
| } else if (initialRadioState == TelephonyManager.RADIO_POWER_ON |
| && mTelephonyManager.getRadioPowerState() != TelephonyManager.RADIO_POWER_ON) { |
| Log.i(TAG, "testLastKnownCountryIso: turning radio back on"); |
| turnRadioOn(callback, TelephonyManager.RADIO_POWER_REASON_USER); |
| } |
| getContext().unregisterReceiver(countryChangedReceiver); |
| } |
| } |
| |
| private static class CarrierInfo { |
| final private int mCallerCarrierId; |
| final private List<String> mSHAIdList; |
| |
| public CarrierInfo(int carrierId, List<String> SHAIds) { |
| mCallerCarrierId = carrierId; |
| mSHAIdList = SHAIds; |
| } |
| |
| public int getCallerCarrierId() { |
| return mCallerCarrierId; |
| } |
| |
| public List<String> getSHAIdList() { |
| return mSHAIdList; |
| } |
| } |
| |
| private static final String CALLER_SHA_1_ID = "callerSHA1Id"; |
| private static final String CALLER_CARRIER_ID = "carrierId"; |
| private CarrierInfo parseJsonForCallerInfo(String callerPackage, JSONObject dataJson) { |
| try { |
| if (dataJson != null && callerPackage != null) { |
| JSONObject callerJSON = dataJson.getJSONObject(callerPackage.trim()); |
| JSONArray callerJSONArray = callerJSON.getJSONArray(CALLER_SHA_1_ID); |
| int carrierId = callerJSON.getInt(CALLER_CARRIER_ID); |
| List<String> appSignatures = new ArrayList<>(); |
| for (int index = 0; index < callerJSONArray.length(); index++) { |
| appSignatures.add((String) callerJSONArray.get(index)); |
| } |
| return new CarrierInfo(carrierId, appSignatures); |
| } |
| } catch (JSONException ex) { |
| Log.e(TAG, "getCallerSignatureInfo: JSONException = " + ex); |
| } |
| return null; |
| } |
| |
| @Test |
| public void testCarrierRestrictionStatusAllowList() throws JSONException { |
| JSONObject testJson = new JSONObject(CARRIER_RESTRICTION_OPERATOR_DETAILS); |
| Set<String> testPkgSet = testJson.keySet(); |
| testPkgSet.remove("_comment"); |
| for (String srcPkg : testPkgSet) { |
| final CarrierInfo testCarrierInfo = parseJsonForCallerInfo(srcPkg, testJson); |
| List<String> shaIdList = ShellIdentityUtils.invokeMethodWithShellPermissions( |
| mTelephonyManager, (tm) -> tm.getShaIdFromAllowList(srcPkg, |
| testCarrierInfo.mCallerCarrierId)); |
| |
| if (shaIdList == null || shaIdList.isEmpty()) { |
| Log.d(TAG, "shaIdList is empty"); |
| fail(); |
| } |
| assertTrue(shaIdList.equals(testCarrierInfo.getSHAIdList())); |
| } |
| } |
| |
| @Test |
| @ApiTest(apis = { |
| "android.telephony.TelephonyManager#requestRadioPowerOffForReason", |
| "android.telephony.TelephonyManager#clearRadioPowerOffForReason", |
| "android.telephony.TelephonyManager#getRadioPowerOffReasons"}) |
| public void testSetRadioPowerForMultiSimDevice() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| if (mTelephonyManager.isMultiSimSupported() != TelephonyManager.MULTISIM_ALLOWED) { |
| Log.d(TAG, "testSetRadioPowerForMultiSimDevice: Multi SIM is not supported"); |
| return; |
| } |
| Integer secondTestSubId = getSecondTestSubId(); |
| if (secondTestSubId == null) { |
| Log.d(TAG, "Need at least 2 active subscriptions to run this test"); |
| return; |
| } |
| Log.d(TAG, "testSetRadioPowerForMultiSimDevice: secondTestSubId=" + secondTestSubId); |
| |
| TelephonyManager secondTelephonyManager = getContext().getSystemService( |
| TelephonyManager.class).createForSubscriptionId(secondTestSubId); |
| ServiceStateRadioStateListener callbackForFirstSub = new ServiceStateRadioStateListener( |
| mTelephonyManager.getServiceState(), mTelephonyManager.getRadioPowerState()); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| tm -> tm.registerTelephonyCallback(Runnable::run, callbackForFirstSub)); |
| ServiceStateRadioStateListener callbackForSecondSub = |
| new ServiceStateRadioStateListener(secondTelephonyManager.getServiceState(), |
| secondTelephonyManager.getRadioPowerState()); |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(secondTelephonyManager, |
| tm -> tm.registerTelephonyCallback(Runnable::run, callbackForSecondSub)); |
| |
| boolean turnedRadioOn = false; |
| if (mTelephonyManager.getRadioPowerState() == TelephonyManager.RADIO_POWER_OFF) { |
| Log.i(TAG, "testSetRadioPowerForMultiSimDevice:" |
| + "turning on radio since it is off"); |
| turnRadioOn(callbackForFirstSub, TelephonyManager.RADIO_POWER_REASON_USER); |
| assertEquals(TelephonyManager.RADIO_POWER_ON, callbackForFirstSub.mRadioPowerState); |
| callbackForSecondSub.waitForRadioStateIntent(TelephonyManager.RADIO_POWER_ON); |
| assertEquals(TelephonyManager.RADIO_POWER_ON, callbackForSecondSub.mRadioPowerState); |
| turnedRadioOn = true; |
| } |
| |
| Log.i(TAG, "testSetRadioPowerForMultiSimDevice:" |
| + "turning radio off due to nearby device ..."); |
| turnRadioOff(callbackForFirstSub, TelephonyManager.RADIO_POWER_REASON_NEARBY_DEVICE); |
| assertRadioOffWithReason(callbackForFirstSub, |
| TelephonyManager.RADIO_POWER_REASON_NEARBY_DEVICE); |
| callbackForSecondSub.waitForRadioStateIntent(TelephonyManager.RADIO_POWER_OFF); |
| assertRadioOffWithReason(secondTelephonyManager, callbackForSecondSub, |
| TelephonyManager.RADIO_POWER_REASON_NEARBY_DEVICE); |
| |
| Log.i(TAG, "testSetRadioPowerForMultiSimDevice: turning on airplane mode ..."); |
| turnRadioOff(callbackForFirstSub, TelephonyManager.RADIO_POWER_REASON_USER); |
| assertRadioOffWithReason(callbackForFirstSub, TelephonyManager.RADIO_POWER_REASON_USER); |
| callbackForSecondSub.waitForRadioStateIntent(TelephonyManager.RADIO_POWER_OFF); |
| assertRadioOffWithReason(secondTelephonyManager, callbackForSecondSub, |
| TelephonyManager.RADIO_POWER_REASON_USER); |
| |
| Log.i(TAG, "testSetRadioPowerForMultiSimDevice: turning off airplane mode ..."); |
| turnRadioOn(callbackForFirstSub, TelephonyManager.RADIO_POWER_REASON_USER); |
| assertRadioOffWithReason(callbackForFirstSub, |
| TelephonyManager.RADIO_POWER_REASON_NEARBY_DEVICE); |
| assertRadioOffWithReason(secondTelephonyManager, callbackForSecondSub, |
| TelephonyManager.RADIO_POWER_REASON_NEARBY_DEVICE); |
| |
| Log.i(TAG, "testSetRadioPowerForMultiSimDevice:" |
| + " turning on radio due to nearby device..."); |
| turnRadioOn(callbackForFirstSub, TelephonyManager.RADIO_POWER_REASON_NEARBY_DEVICE); |
| assertEquals(TelephonyManager.RADIO_POWER_ON, callbackForFirstSub.mRadioPowerState); |
| callbackForSecondSub.waitForRadioStateIntent(TelephonyManager.RADIO_POWER_ON); |
| assertEquals(TelephonyManager.RADIO_POWER_ON, callbackForSecondSub.mRadioPowerState); |
| |
| if (turnedRadioOn) { |
| Log.i(TAG, "testSetRadioPowerForMultiSimDevice: turning radio back off"); |
| turnRadioOff(callbackForFirstSub, TelephonyManager.RADIO_POWER_REASON_USER); |
| callbackForSecondSub.waitForRadioStateIntent(TelephonyManager.RADIO_POWER_OFF); |
| } |
| } |
| |
| @Test |
| @ApiTest(apis = { |
| "android.telephony.TelephonyManager#isCellularIdentifierDisclosureNotificationEnabled", |
| "android.telephony.TelephonyManager#enableCellularIdentifierDisclosureNotifications"}) |
| @RequiresFlagsEnabled(Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY) |
| public void testEnableCellularIdentifierDisclosureNotifications() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| if (mNetworkHalVersion < RADIO_HAL_VERSION_2_2) { |
| Log.d(TAG, |
| "Skipping test since modem does not support IRadioNetwork HAL v2.2"); |
| return; |
| } |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.enableCellularIdentifierDisclosureNotifications(true)); |
| boolean enabled = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.isCellularIdentifierDisclosureNotificationEnabled()); |
| assertTrue(enabled); |
| |
| ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, |
| (tm) -> tm.enableCellularIdentifierDisclosureNotifications(false)); |
| enabled = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, |
| (tm) -> tm.isCellularIdentifierDisclosureNotificationEnabled()); |
| assertFalse(enabled); |
| } |
| |
| @Test |
| @ApiTest(apis = { |
| "android.telephony.TelephonyManager#isCellularIdentifierDisclosureNotificationEnabled", |
| "android.telephony.TelephonyManager#enableCellularIdentifierDisclosureNotifications"}) |
| @RequiresFlagsEnabled(Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY) |
| public void testCellularIdentifierDisclosureNotificationsPermissions() { |
| assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); |
| if (mNetworkHalVersion < RADIO_HAL_VERSION_2_2) { |
| Log.d(TAG, |
| "Skipping test since modem does not support IRadioNetwork HAL v2.2"); |
| return; |
| } |
| |
| assertThrows(SecurityException.class, () -> { |
| mTelephonyManager.enableCellularIdentifierDisclosureNotifications(true); |
| } |
| ); |
| |
| assertThrows(SecurityException.class, () -> { |
| mTelephonyManager.isCellularIdentifierDisclosureNotificationEnabled(); |
| } |
| ); |
| } |
| |
| private Integer getSecondTestSubId() { |
| try { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .adoptShellPermissionIdentity( |
| android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE); |
| for (int subId : mSubscriptionManager.getActiveSubscriptionIdList()) { |
| if (subId != mTestSub) { |
| return subId; |
| } |
| } |
| } catch (SecurityException e) { |
| fail("SubscriptionManager#getActiveSubscriptionIdList requires " |
| + "READ_PRIVILEGED_PHONE_STATE"); |
| } finally { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .dropShellPermissionIdentity(); |
| } |
| return null; |
| } |
| } |