Merge "Allow privileged apps to call getUiccCardsInfo"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9f9269e..f68b72e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -194,6 +194,7 @@
<uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
<uses-permission android:name="android.permission.BIND_TELEPHONY_DATA_SERVICE" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+ <uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />
<application android:name="PhoneApp"
android:persistent="true"
diff --git a/res/xml/phone_account_settings.xml b/res/xml/phone_account_settings.xml
index ae3e9d9..d230328 100644
--- a/res/xml/phone_account_settings.xml
+++ b/res/xml/phone_account_settings.xml
@@ -29,6 +29,7 @@
android:order="1" />
<PreferenceScreen
+ android:key="phone_accounts_all_calling_accounts"
android:title="@string/phone_accounts_all_calling_accounts"
android:summary="@string/phone_accounts_all_calling_accounts_summary"
android:persistent="false"
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index a5cd06b..3d82e74 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -598,14 +598,22 @@
airplaneMode = AIRPLANE_ON;
}
handleAirplaneModeChange(context, airplaneMode);
- } else if ((action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) &&
- (mPUKEntryActivity != null)) {
- // if an attempt to un-PUK-lock the device was made, while we're
- // receiving this state change notification, notify the handler.
- // NOTE: This is ONLY triggered if an attempt to un-PUK-lock has
- // been attempted.
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_SIM_STATE_CHANGED,
- intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)));
+ } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
+ // re-register as it may be a new IccCard
+ int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY,
+ SubscriptionManager.INVALID_PHONE_INDEX);
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ PhoneUtils.unregisterIccStatus(mHandler, phoneId);
+ PhoneUtils.registerIccStatus(mHandler, EVENT_SIM_NETWORK_LOCKED, phoneId);
+ }
+ if (mPUKEntryActivity != null) {
+ // if an attempt to un-PUK-lock the device was made, while we're
+ // receiving this state change notification, notify the handler.
+ // NOTE: This is ONLY triggered if an attempt to un-PUK-lock has
+ // been attempted.
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_SIM_STATE_CHANGED,
+ intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)));
+ }
} else if (action.equals(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED)) {
String newPhone = intent.getStringExtra(PhoneConstants.PHONE_NAME_KEY);
Log.d(LOG_TAG, "Radio technology switched. Now " + newPhone + " is active.");
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 393436d..13accc9 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -35,6 +35,7 @@
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -135,6 +136,7 @@
import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.euicc.EuiccConnector;
+import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.uicc.IccIoResult;
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.telephony.uicc.SIMRecords;
@@ -156,6 +158,7 @@
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;
@@ -1103,6 +1106,7 @@
ar = (AsyncResult) msg.obj;
request = (MainThreadRequest) ar.userObj;
request.result = (ar.exception == null);
+ updateModemStateMetrics();
notifyRequester(request);
break;
default:
@@ -1841,9 +1845,21 @@
public Bundle getCellLocation(String callingPackage) {
mApp.getSystemService(AppOpsManager.class)
.checkPackage(Binder.getCallingUid(), callingPackage);
- if (!LocationAccessPolicy.canAccessCellLocation(mApp, callingPackage,
- Binder.getCallingUid(), Binder.getCallingPid(), true)) {
- return null;
+
+ LocationAccessPolicy.LocationPermissionResult locationResult =
+ LocationAccessPolicy.checkLocationPermission(mApp,
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(callingPackage)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid())
+ .setMethod("getCellLocation")
+ .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+ .build());
+ switch (locationResult) {
+ case DENIED_HARD:
+ throw new SecurityException("Not allowed to access cell location");
+ case DENIED_SOFT:
+ return new Bundle();
}
WorkSource workSource = getWorkSource(Binder.getCallingUid());
@@ -1993,9 +2009,21 @@
public List<CellInfo> getAllCellInfo(String callingPackage) {
mApp.getSystemService(AppOpsManager.class)
.checkPackage(Binder.getCallingUid(), callingPackage);
- if (!LocationAccessPolicy.canAccessCellLocation(mApp,
- callingPackage, Binder.getCallingUid(), Binder.getCallingPid(), true)) {
- return null;
+
+ LocationAccessPolicy.LocationPermissionResult locationResult =
+ LocationAccessPolicy.checkLocationPermission(mApp,
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(callingPackage)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid())
+ .setMethod("getAllCellInfo")
+ .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+ .build());
+ switch (locationResult) {
+ case DENIED_HARD:
+ throw new SecurityException("Not allowed to access cell info");
+ case DENIED_SOFT:
+ return new ArrayList<>();
}
final int targetSdk = getTargetSdk(callingPackage);
@@ -2036,9 +2064,21 @@
int subId, ICellInfoCallback cb, String callingPackage, WorkSource workSource) {
mApp.getSystemService(AppOpsManager.class)
.checkPackage(Binder.getCallingUid(), callingPackage);
- if (!LocationAccessPolicy.canAccessCellLocation(mApp, callingPackage,
- Binder.getCallingUid(), Binder.getCallingPid(), true)) {
- return;
+
+ LocationAccessPolicy.LocationPermissionResult locationResult =
+ LocationAccessPolicy.checkLocationPermission(mApp,
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(callingPackage)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid())
+ .setMethod("requestCellInfoUpdate")
+ .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+ .build());
+ switch (locationResult) {
+ case DENIED_HARD:
+ throw new SecurityException("Not allowed to access cell info");
+ case DENIED_SOFT:
+ return;
}
final Phone phone = getPhone(subId);
@@ -4187,9 +4227,24 @@
* Scans for available networks.
*/
@Override
- public CellNetworkScanResult getCellNetworkScanResults(int subId) {
+ public CellNetworkScanResult getCellNetworkScanResults(int subId, String callingPackage) {
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
mApp, subId, "getCellNetworkScanResults");
+ LocationAccessPolicy.LocationPermissionResult locationResult =
+ LocationAccessPolicy.checkLocationPermission(mApp,
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(callingPackage)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid())
+ .setMethod("getCellNetworkScanResults")
+ .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+ .build());
+ switch (locationResult) {
+ case DENIED_HARD:
+ throw new SecurityException("Not allowed to access scan results -- location");
+ case DENIED_SOFT:
+ return null;
+ }
long identity = Binder.clearCallingIdentity();
try {
@@ -4212,17 +4267,29 @@
*/
@Override
public int requestNetworkScan(int subId, NetworkScanRequest request, Messenger messenger,
- IBinder binder) {
+ IBinder binder, String callingPackage) {
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
mApp, subId, "requestNetworkScan");
- final long identity = Binder.clearCallingIdentity();
- try {
- return mNetworkScanRequestTracker.startNetworkScan(
- request, messenger, binder, getPhone(subId));
- } finally {
- Binder.restoreCallingIdentity(identity);
+ LocationAccessPolicy.LocationPermissionResult locationResult =
+ LocationAccessPolicy.checkLocationPermission(mApp,
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(callingPackage)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid())
+ .setMethod("requestNetworkScan")
+ .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+ .build());
+ switch (locationResult) {
+ case DENIED_HARD:
+ throw new SecurityException("Not allowed to request network scan -- location");
+ case DENIED_SOFT:
+ return -1;
}
+
+ return mNetworkScanRequestTracker.startNetworkScan(
+ request, messenger, binder, getPhone(subId),
+ callingPackage);
}
/**
@@ -5263,6 +5330,31 @@
return null;
}
+ LocationAccessPolicy.LocationPermissionResult fineLocationResult =
+ LocationAccessPolicy.checkLocationPermission(mApp,
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(callingPackage)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid())
+ .setMethod("getServiceStateForSubscriber")
+ .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+ .build());
+
+ LocationAccessPolicy.LocationPermissionResult coarseLocationResult =
+ LocationAccessPolicy.checkLocationPermission(mApp,
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(callingPackage)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid())
+ .setMethod("getServiceStateForSubscriber")
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q)
+ .build());
+ // We don't care about hard or soft here -- all we need to know is how much info to scrub.
+ boolean hasFinePermission =
+ fineLocationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+ boolean hasCoarsePermission =
+ coarseLocationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+
final long identity = Binder.clearCallingIdentity();
try {
final Phone phone = getPhone(subId);
@@ -5270,7 +5362,13 @@
return null;
}
- return phone.getServiceState();
+ ServiceState ss = phone.getServiceState();
+
+ // Scrub out the location info in ServiceState depending on what level of access
+ // the caller has.
+ if (hasFinePermission) return ss;
+ if (hasCoarsePermission) return ss.sanitizeLocationInfo(false);
+ return ss.sanitizeLocationInfo(true);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -6527,7 +6625,7 @@
/**
* Get whether reboot is required or not after making changes to modem configurations.
- * Return value defaults to false
+ * Return value defaults to true
*/
@Override
public boolean isRebootRequiredForModemConfigChange() {
@@ -6540,4 +6638,30 @@
}
}
+ private void updateModemStateMetrics() {
+ TelephonyMetrics metrics = TelephonyMetrics.getInstance();
+ // TODO: check the state for each modem if the api is ready.
+ metrics.updateEnabledModemBitmap((1 << TelephonyManager.from(mApp).getPhoneCount()) - 1);
+ }
+
+ @Override
+ public int[] getSlotsMapping() {
+ enforceReadPrivilegedPermission("getSlotsMapping");
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ int phoneCount = TelephonyManager.getDefault().getPhoneCount();
+ // All logical slots should have a mapping to a physical slot.
+ int[] logicalSlotsMapping = new int[phoneCount];
+ UiccSlotInfo[] slotInfos = getUiccSlotsInfo();
+ for (int i = 0; i < slotInfos.length; i++) {
+ if (SubscriptionManager.isValidPhoneId(slotInfos[i].getLogicalSlotIdx())) {
+ logicalSlotsMapping[slotInfos[i].getLogicalSlotIdx()] = i;
+ }
+ }
+ return logicalSlotsMapping;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index c1bd1b6..6f1f0a6 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -19,7 +19,6 @@
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
-import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
@@ -1284,6 +1283,34 @@
}
/**
+ * Register ICC status for all phones.
+ */
+ static final void registerIccStatus(Handler handler, int event, int phoneId) {
+ Phone[] phones = PhoneFactory.getPhones();
+ IccCard sim = phones[phoneId].getIccCard();
+ if (sim != null) {
+ if (VDBG) {
+ Log.v(LOG_TAG, "register for ICC status, phone " + phones[phoneId].getPhoneId());
+ }
+ sim.registerForNetworkLocked(handler, event, phones[phoneId]);
+ }
+ }
+
+ /**
+ * Unregister ICC status for a specific phone.
+ */
+ static final void unregisterIccStatus(Handler handler, int phoneId) {
+ Phone[] phones = PhoneFactory.getPhones();
+ IccCard sim = phones[phoneId].getIccCard();
+ if (sim != null) {
+ if (VDBG) {
+ Log.v(LOG_TAG, "unregister for ICC status, phone " + phones[phoneId].getPhoneId());
+ }
+ sim.unregisterForNetworkLocked(handler);
+ }
+ }
+
+ /**
* Set the radio power on/off state for all phones.
*
* @param enabled true means on, false means off.
diff --git a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
index 41063e2..ca45b31 100644
--- a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
+++ b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
@@ -18,7 +18,6 @@
import android.telecom.TelecomManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
-import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -45,7 +44,7 @@
"phone_accounts_accounts_list_category_key";
private static final String DEFAULT_OUTGOING_ACCOUNT_KEY = "default_outgoing_account";
- private static final String ALL_CALLING_ACCOUNTS_KEY = "phone_account_all_calling_accounts";
+ private static final String ALL_CALLING_ACCOUNTS_KEY = "phone_accounts_all_calling_accounts";
private static final String SIP_SETTINGS_CATEGORY_PREF_KEY =
"phone_accounts_sip_settings_category_key";
@@ -72,6 +71,7 @@
private PreferenceCategory mAccountList;
private AccountSelectionPreference mDefaultOutgoingAccount;
+ private Preference mAllCallingAccounts;
private ListPreference mUseSipCalling;
private SwitchPreference mSipReceiveCallsPreference;
@@ -132,6 +132,9 @@
*/
mAccountList = (PreferenceCategory) getPreferenceScreen().findPreference(
ACCOUNTS_LIST_CATEGORY_KEY);
+ mDefaultOutgoingAccount = (AccountSelectionPreference)
+ getPreferenceScreen().findPreference(DEFAULT_OUTGOING_ACCOUNT_KEY);
+ mAllCallingAccounts = getPreferenceScreen().findPreference(ALL_CALLING_ACCOUNTS_KEY);
updateAccounts();
@@ -397,25 +400,21 @@
// Initialize the account list with the set of enabled & SIM accounts.
initAccountList(enabledAccounts);
- mDefaultOutgoingAccount = (AccountSelectionPreference)
- getPreferenceScreen().findPreference(DEFAULT_OUTGOING_ACCOUNT_KEY);
- if (mDefaultOutgoingAccount != null) {
- mDefaultOutgoingAccount.setListener(this);
-
- // Only show the 'Make Calls With..." option if there are multiple accounts.
- if (enabledAccounts.size() > 1) {
- updateDefaultOutgoingAccountsModel();
- } else {
- mAccountList.removePreference(mDefaultOutgoingAccount);
- }
+ mDefaultOutgoingAccount.setListener(this);
+ // Only show the 'Make Calls With..." option if there are multiple accounts.
+ if (enabledAccounts.size() > 1) {
+ mAccountList.addPreference(mDefaultOutgoingAccount);
+ updateDefaultOutgoingAccountsModel();
+ } else {
+ mAccountList.removePreference(mDefaultOutgoingAccount);
}
- Preference allAccounts =
- getPreferenceScreen().findPreference(ALL_CALLING_ACCOUNTS_KEY);
// If there are no third party (nonSim) accounts,
// then don't show enable/disable dialog.
- if (allNonSimAccounts.isEmpty() && allAccounts != null) {
- mAccountList.removePreference(allAccounts);
+ if (!allNonSimAccounts.isEmpty()) {
+ mAccountList.addPreference(mAllCallingAccounts);
+ } else {
+ mAccountList.removePreference(mAllCallingAccounts);
}
} else {
getPreferenceScreen().removePreference(mAccountList);
diff --git a/src/com/android/services/telephony/RadioOnHelper.java b/src/com/android/services/telephony/RadioOnHelper.java
index cd08289..288c72c 100644
--- a/src/com/android/services/telephony/RadioOnHelper.java
+++ b/src/com/android/services/telephony/RadioOnHelper.java
@@ -93,13 +93,12 @@
* get an onServiceStateChanged() callback when the radio successfully comes up.
*/
private void powerOnRadio() {
- Log.d(this, "powerOnRadio().");
// If airplane mode is on, we turn it off the same way that the Settings activity turns it
// off.
if (Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 0) > 0) {
- Log.d(this, "==> Turning off airplane mode.");
+ Log.d(this, "==> Turning off airplane mode for emergency call.");
// Change the system setting
Settings.Global.putInt(mContext.getContentResolver(),
diff --git a/src/com/android/services/telephony/RadioOnStateListener.java b/src/com/android/services/telephony/RadioOnStateListener.java
index 91a7d77..729f6a9 100644
--- a/src/com/android/services/telephony/RadioOnStateListener.java
+++ b/src/com/android/services/telephony/RadioOnStateListener.java
@@ -46,16 +46,16 @@
}
// Number of times to retry the call, and time between retry attempts.
+ // not final for testing
private static int MAX_NUM_RETRIES = 5;
+ // not final for testing
private static long TIME_BETWEEN_RETRIES_MILLIS = 5000; // msec
// Handler message codes; see handleMessage()
- @VisibleForTesting
- public static final int MSG_START_SEQUENCE = 1;
+ private static final int MSG_START_SEQUENCE = 1;
@VisibleForTesting
public static final int MSG_SERVICE_STATE_CHANGED = 2;
- @VisibleForTesting
- public static final int MSG_RETRY_TIMEOUT = 3;
+ private static final int MSG_RETRY_TIMEOUT = 3;
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index e0db44e..6d7c1f0 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -40,8 +40,8 @@
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
import android.text.TextUtils;
-import android.util.FeatureFlagUtils;
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
@@ -68,6 +68,7 @@
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Queue;
import java.util.regex.Pattern;
@@ -208,26 +209,47 @@
};
// TelephonyManager Proxy interface for testing
+ @VisibleForTesting
public interface TelephonyManagerProxy {
int getPhoneCount();
boolean hasIccCard(int slotId);
+ boolean isCurrentEmergencyNumber(String number);
+ Map<Integer, List<EmergencyNumber>> getCurrentEmergencyNumberList();
}
- private TelephonyManagerProxy mTelephonyManagerProxy = new TelephonyManagerProxy() {
- private final TelephonyManager sTelephonyManager = TelephonyManager.getDefault();
+ private TelephonyManagerProxy mTelephonyManagerProxy;
+
+ private class TelephonyManagerProxyImpl implements TelephonyManagerProxy {
+ private final TelephonyManager mTelephonyManager;
+
+
+ TelephonyManagerProxyImpl(Context context) {
+ mTelephonyManager = new TelephonyManager(context);
+ }
@Override
public int getPhoneCount() {
- return sTelephonyManager.getPhoneCount();
+ return mTelephonyManager.getPhoneCount();
}
@Override
public boolean hasIccCard(int slotId) {
- return sTelephonyManager.hasIccCard(slotId);
+ return mTelephonyManager.hasIccCard(slotId);
}
- };
+
+ @Override
+ public boolean isCurrentEmergencyNumber(String number) {
+ return mTelephonyManager.isCurrentEmergencyNumber(number);
+ }
+
+ @Override
+ public Map<Integer, List<EmergencyNumber>> getCurrentEmergencyNumberList() {
+ return mTelephonyManager.getCurrentEmergencyNumberList();
+ }
+ }
//PhoneFactory proxy interface for testing
+ @VisibleForTesting
public interface PhoneFactoryProxy {
Phone getPhone(int index);
Phone getDefaultPhone();
@@ -286,6 +308,7 @@
public void onCreate() {
super.onCreate();
Log.initLogging(this);
+ setTelephonyManagerProxy(new TelephonyManagerProxyImpl(getApplicationContext()));
mExpectedComponentName = new ComponentName(this, this.getClass());
mEmergencyTonePlayer = new EmergencyTonePlayer(this);
TelecomAccountRegistry.getInstance(this).setTelephonyConnectionService(this);
@@ -387,9 +410,13 @@
}
}
+ final boolean isEmergencyNumber = mTelephonyManagerProxy.isCurrentEmergencyNumber(number);
+ // Find out if this is a test emergency number
+ final boolean isTestEmergencyNumber = isEmergencyNumberTestNumber(number);
+
// Convert into emergency number if necessary
// This is required in some regions (e.g. Taiwan).
- if (!PhoneNumberUtils.isLocalEmergencyNumber(this, number)) {
+ if (isEmergencyNumber) {
final Phone phone = getPhoneForAccount(request.getAccountHandle(), false,
handle.getSchemeSpecificPart());
// We only do the conversion if the phone is not in service. The un-converted
@@ -408,9 +435,6 @@
}
final String numberToDial = number;
- final boolean isEmergencyNumber =
- PhoneNumberUtils.isLocalEmergencyNumber(this, numberToDial);
-
final boolean isAirplaneModeOn = Settings.Global.getInt(getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 0) > 0;
@@ -437,7 +461,12 @@
@Override
public boolean isOkToCall(Phone phone, int serviceState) {
- if (isEmergencyNumber) {
+ // HAL 1.4 introduced a new variant of dial for emergency calls, which includes
+ // an isTesting parameter. For HAL 1.4+, do not wait for IN_SERVICE, this will
+ // be handled at the RIL/vendor level by emergencyDial(...).
+ boolean waitForInServiceToDialEmergency = isTestEmergencyNumber
+ && phone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_4);
+ if (isEmergencyNumber && !waitForInServiceToDialEmergency) {
// We currently only look to make sure that the radio is on before dialing.
// We should be able to make emergency calls at any time after the radio has
// been powered on and isn't in the UNAVAILABLE state, even if it is
@@ -445,9 +474,10 @@
return (phone.getState() == PhoneConstants.State.OFFHOOK)
|| phone.getServiceState().getState() != ServiceState.STATE_POWER_OFF;
} else {
- // It is not an emergency number, so wait until we are in service and ready
- // to make calls. This can happen when we power down the radio on bluetooth
- // to save power on watches.
+ // Wait until we are in service and ready to make calls. This can happen
+ // when we power down the radio on bluetooth to save power on watches or if
+ // it is a test emergency number and we have to wait for the device to move
+ // IN_SERVICE before the call can take place over normal routing.
return (phone.getState() == PhoneConstants.State.OFFHOOK)
|| serviceState == ServiceState.STATE_IN_SERVICE;
}
@@ -487,6 +517,24 @@
}
}
+ private boolean isEmergencyNumberTestNumber(String number) {
+ Map<Integer, List<EmergencyNumber>> list =
+ mTelephonyManagerProxy.getCurrentEmergencyNumberList();
+ // Do not worry about which subscription the test emergency call is on yet, only detect that
+ // it is an emergency.
+ for (Integer sub : list.keySet()) {
+ for (EmergencyNumber eNumber : list.get(sub)) {
+ if (number.equals(eNumber.getNumber())
+ && eNumber.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST)) {
+ Log.i(this, "isEmergencyNumberTestNumber: " + number + " has been detected as "
+ + "a test emergency number.,");
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/**
* Whether the cellular radio is power off because the device is on Bluetooth.
*/
diff --git a/testapps/TelephonyManagerTestApp/res/layout/calling_method.xml b/testapps/TelephonyManagerTestApp/res/layout/calling_method.xml
index 5145a63..6dd8bc2 100644
--- a/testapps/TelephonyManagerTestApp/res/layout/calling_method.xml
+++ b/testapps/TelephonyManagerTestApp/res/layout/calling_method.xml
@@ -73,7 +73,6 @@
android:layout_height="50dip">
</Button>
-
<ScrollView
android:id="@+id/return_value_wrapper"
android:layout_width="fill_parent"
diff --git a/testapps/TelephonyRegistryTestApp/AndroidManifest.xml b/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
index 1cfd3ba..550c9f0 100644
--- a/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
+++ b/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
@@ -16,6 +16,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.phone.testapps.telephonyregistry">
+ <uses-sdk android:minSdkVersion="25"
+ android:targetSdkVersion="25"/>
+
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java b/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
index 74cafcd..96f8bf7 100644
--- a/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
+++ b/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
@@ -24,6 +24,7 @@
import android.telephony.CellInfo;
import android.telephony.CellLocation;
import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.util.SparseArray;
import android.widget.Button;
@@ -76,6 +77,11 @@
notify("onSrvccStateChanged", srvccState);
}
+ @Override
+ public void onServiceStateChanged(ServiceState state) {
+ notify("onServiceStateChanged", state);
+ }
+
private void notify(String method, Object data) {
Notification.Builder builder = new Notification.Builder(TelephonyRegistryTestApp.this,
NOTIFICATION_CHANNEL);
diff --git a/tests/src/com/android/phone/LocationAccessPolicyTest.java b/tests/src/com/android/phone/LocationAccessPolicyTest.java
new file mode 100644
index 0000000..9938bf2
--- /dev/null
+++ b/tests/src/com/android/phone/LocationAccessPolicyTest.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.location.LocationManager;
+import android.os.Build;
+import android.os.UserHandle;
+import android.telephony.LocationAccessPolicy;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+@RunWith(Parameterized.class)
+public class LocationAccessPolicyTest {
+ private static class Scenario {
+ static class Builder {
+ private int mAppSdkLevel;
+ private boolean mAppHasFineManifest = false;
+ private boolean mAppHasCoarseManifest = false;
+ private int mFineAppOp = AppOpsManager.MODE_IGNORED;
+ private int mCoarseAppOp = AppOpsManager.MODE_IGNORED;
+ private boolean mIsDynamicLocationEnabled;
+ private LocationAccessPolicy.LocationPermissionQuery mQuery;
+ private LocationAccessPolicy.LocationPermissionResult mExpectedResult;
+ private String mName;
+
+ public Builder setAppSdkLevel(int appSdkLevel) {
+ mAppSdkLevel = appSdkLevel;
+ return this;
+ }
+
+ public Builder setAppHasFineManifest(boolean appHasFineManifest) {
+ mAppHasFineManifest = appHasFineManifest;
+ return this;
+ }
+
+ public Builder setAppHasCoarseManifest(
+ boolean appHasCoarseManifest) {
+ mAppHasCoarseManifest = appHasCoarseManifest;
+ return this;
+ }
+
+ public Builder setFineAppOp(int fineAppOp) {
+ mFineAppOp = fineAppOp;
+ return this;
+ }
+
+ public Builder setCoarseAppOp(int coarseAppOp) {
+ mCoarseAppOp = coarseAppOp;
+ return this;
+ }
+
+ public Builder setIsDynamicLocationEnabled(
+ boolean isDynamicLocationEnabled) {
+ mIsDynamicLocationEnabled = isDynamicLocationEnabled;
+ return this;
+ }
+
+ public Builder setQuery(
+ LocationAccessPolicy.LocationPermissionQuery query) {
+ mQuery = query;
+ return this;
+ }
+
+ public Builder setExpectedResult(
+ LocationAccessPolicy.LocationPermissionResult expectedResult) {
+ mExpectedResult = expectedResult;
+ return this;
+ }
+
+ public Builder setName(String name) {
+ mName = name;
+ return this;
+ }
+
+ public Scenario build() {
+ return new Scenario(mAppSdkLevel, mAppHasFineManifest, mAppHasCoarseManifest,
+ mFineAppOp, mCoarseAppOp, mIsDynamicLocationEnabled, mQuery,
+ mExpectedResult, mName);
+ }
+ }
+ int appSdkLevel;
+ boolean appHasFineManifest;
+ boolean appHasCoarseManifest;
+ int fineAppOp;
+ int coarseAppOp;
+ boolean isDynamicLocationEnabled;
+ LocationAccessPolicy.LocationPermissionQuery query;
+ LocationAccessPolicy.LocationPermissionResult expectedResult;
+ String name;
+
+ private Scenario(int appSdkLevel, boolean appHasFineManifest, boolean appHasCoarseManifest,
+ int fineAppOp, int coarseAppOp,
+ boolean isDynamicLocationEnabled,
+ LocationAccessPolicy.LocationPermissionQuery query,
+ LocationAccessPolicy.LocationPermissionResult expectedResult,
+ String name) {
+ this.appSdkLevel = appSdkLevel;
+ this.appHasFineManifest = appHasFineManifest;
+ this.appHasCoarseManifest = appHasFineManifest || appHasCoarseManifest;
+ this.fineAppOp = fineAppOp;
+ this.coarseAppOp = coarseAppOp == AppOpsManager.MODE_ALLOWED ? coarseAppOp : fineAppOp;
+ this.isDynamicLocationEnabled = isDynamicLocationEnabled;
+ this.query = query;
+ this.expectedResult = expectedResult;
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ @Mock Context mContext;
+ @Mock AppOpsManager mAppOpsManager;
+ @Mock LocationManager mLocationManager;
+ @Mock PackageManager mPackageManager;
+ Scenario mScenario;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mockContextSystemService(AppOpsManager.class, mAppOpsManager);
+ mockContextSystemService(LocationManager.class, mLocationManager);
+ mockContextSystemService(PackageManager.class, mPackageManager);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ }
+
+ private <T> void mockContextSystemService(Class<T> clazz , T obj) {
+ when(mContext.getSystemServiceName(eq(clazz))).thenReturn(clazz.getSimpleName());
+ when(mContext.getSystemService(clazz.getSimpleName())).thenReturn(obj);
+ }
+
+ public LocationAccessPolicyTest(Scenario scenario) {
+ mScenario = scenario;
+ }
+
+
+ @Test
+ public void test() {
+ setupScenario(mScenario);
+ assertEquals(mScenario.expectedResult,
+ LocationAccessPolicy.checkLocationPermission(mContext, mScenario.query));
+ }
+
+ private void setupScenario(Scenario s) {
+ when(mContext.checkPermission(eq(Manifest.permission.ACCESS_FINE_LOCATION),
+ anyInt(), anyInt())).thenReturn(s.appHasFineManifest
+ ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
+
+ when(mContext.checkPermission(eq(Manifest.permission.ACCESS_COARSE_LOCATION),
+ anyInt(), anyInt())).thenReturn(s.appHasCoarseManifest
+ ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
+
+ when(mAppOpsManager.noteOpNoThrow(eq(AppOpsManager.OP_FINE_LOCATION),
+ anyInt(), anyString()))
+ .thenReturn(s.fineAppOp);
+ when(mAppOpsManager.noteOpNoThrow(eq(AppOpsManager.OP_COARSE_LOCATION),
+ anyInt(), anyString()))
+ .thenReturn(s.coarseAppOp);
+
+ if (s.isDynamicLocationEnabled) {
+ when(mLocationManager.isLocationEnabledForUser(any(UserHandle.class))).thenReturn(true);
+ when(mContext.checkPermission(eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL),
+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+ } else {
+ when(mLocationManager.isLocationEnabledForUser(any(UserHandle.class)))
+ .thenReturn(false);
+ when(mContext.checkPermission(eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL),
+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
+ }
+
+ ApplicationInfo fakeAppInfo = new ApplicationInfo();
+ fakeAppInfo.targetSdkVersion = s.appSdkLevel;
+
+ try {
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt()))
+ .thenReturn(fakeAppInfo);
+ } catch (Exception e) {
+ // this is a formality
+ }
+ }
+
+ private static LocationAccessPolicy.LocationPermissionQuery.Builder getDefaultQueryBuilder() {
+ return new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setMethod("test")
+ .setCallingPackage("com.android.test")
+ .setCallingPid(10001)
+ .setCallingUid(10001);
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<Scenario> getScenarios() {
+ List<Scenario> scenarios = new ArrayList<>();
+ scenarios.add(new Scenario.Builder()
+ .setName("System location is off")
+ .setAppHasFineManifest(true)
+ .setFineAppOp(AppOpsManager.MODE_ALLOWED)
+ .setAppSdkLevel(Build.VERSION_CODES.P)
+ .setIsDynamicLocationEnabled(false)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForFine(Build.VERSION_CODES.N)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.N).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.DENIED_SOFT)
+ .build());
+
+ scenarios.add(new Scenario.Builder()
+ .setName("App on latest SDK level has all proper permissions for fine")
+ .setAppHasFineManifest(true)
+ .setFineAppOp(AppOpsManager.MODE_ALLOWED)
+ .setAppSdkLevel(Build.VERSION_CODES.P)
+ .setIsDynamicLocationEnabled(true)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForFine(Build.VERSION_CODES.N)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.N).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.ALLOWED)
+ .build());
+
+ scenarios.add(new Scenario.Builder()
+ .setName("App on older SDK level missing permissions for fine but has coarse")
+ .setAppHasCoarseManifest(true)
+ .setCoarseAppOp(AppOpsManager.MODE_ALLOWED)
+ .setAppSdkLevel(Build.VERSION_CODES.JELLY_BEAN)
+ .setIsDynamicLocationEnabled(true)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForFine(Build.VERSION_CODES.M)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.JELLY_BEAN).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.ALLOWED)
+ .build());
+
+ scenarios.add(new Scenario.Builder()
+ .setName("App on latest SDK level missing fine app ops permission")
+ .setAppHasFineManifest(true)
+ .setFineAppOp(AppOpsManager.MODE_ERRORED)
+ .setAppSdkLevel(Build.VERSION_CODES.P)
+ .setIsDynamicLocationEnabled(true)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForFine(Build.VERSION_CODES.N)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.N).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.DENIED_HARD)
+ .build());
+
+ scenarios.add(new Scenario.Builder()
+ .setName("App has coarse permission but fine permission isn't being enforced yet")
+ .setAppHasCoarseManifest(true)
+ .setCoarseAppOp(AppOpsManager.MODE_ALLOWED)
+ .setAppSdkLevel(LocationAccessPolicy.MAX_SDK_FOR_ANY_ENFORCEMENT + 1)
+ .setIsDynamicLocationEnabled(true)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForFine(
+ LocationAccessPolicy.MAX_SDK_FOR_ANY_ENFORCEMENT + 1)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.N).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.ALLOWED)
+ .build());
+
+ scenarios.add(new Scenario.Builder()
+ .setName("App on latest SDK level has coarse but missing fine when fine is req.")
+ .setAppHasCoarseManifest(true)
+ .setCoarseAppOp(AppOpsManager.MODE_ALLOWED)
+ .setAppSdkLevel(Build.VERSION_CODES.P)
+ .setIsDynamicLocationEnabled(true)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForFine(Build.VERSION_CODES.P)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.N).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.DENIED_HARD)
+ .build());
+
+ scenarios.add(new Scenario.Builder()
+ .setName("App on latest SDK level has MODE_IGNORED for app ops on fine")
+ .setAppHasCoarseManifest(true)
+ .setCoarseAppOp(AppOpsManager.MODE_ALLOWED)
+ .setFineAppOp(AppOpsManager.MODE_IGNORED)
+ .setAppSdkLevel(Build.VERSION_CODES.P)
+ .setIsDynamicLocationEnabled(true)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForFine(Build.VERSION_CODES.P)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.O).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.DENIED_HARD)
+ .build());
+
+ scenarios.add(new Scenario.Builder()
+ .setName("App has no permissions but it's sdk level grandfathers it in")
+ .setAppSdkLevel(Build.VERSION_CODES.N)
+ .setIsDynamicLocationEnabled(true)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.O).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.ALLOWED)
+ .build());
+
+ scenarios.add(new Scenario.Builder()
+ .setName("App on latest SDK level has proper permissions for coarse")
+ .setAppHasCoarseManifest(true)
+ .setCoarseAppOp(AppOpsManager.MODE_ALLOWED)
+ .setAppSdkLevel(Build.VERSION_CODES.P)
+ .setIsDynamicLocationEnabled(true)
+ .setQuery(getDefaultQueryBuilder()
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.P).build())
+ .setExpectedResult(LocationAccessPolicy.LocationPermissionResult.ALLOWED)
+ .build());
+ return scenarios;
+ }
+}
diff --git a/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java b/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
index fb214cc..d9de2e8 100644
--- a/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
+++ b/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
@@ -16,17 +16,25 @@
package com.android.services.telephony;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.os.AsyncResult;
import android.os.Handler;
-import android.telephony.ServiceState;
import android.support.test.runner.AndroidJUnit4;
-import android.support.test.filters.FlakyTest;
+import android.telephony.ServiceState;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.TelephonyTestBase;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.ServiceStateTracker;
import org.junit.After;
import org.junit.Before;
@@ -34,16 +42,6 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Matchers.isNull;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.when;
-
/**
* Tests the RadioOnStateListener, which listens to one Phone and waits until its service
* state changes to accepting emergency calls or in service. If it can not find a tower to camp onto
@@ -52,21 +50,26 @@
@RunWith(AndroidJUnit4.class)
public class RadioOnStateListenerTest extends TelephonyTestBase {
- private static final long TIMEOUT_MS = 100;
+ private static final long TIMEOUT_MS = 1000;
@Mock Phone mMockPhone;
@Mock RadioOnStateListener.Callback mCallback;
RadioOnStateListener mListener;
+ @Override
@Before
public void setUp() throws Exception {
super.setUp();
mListener = new RadioOnStateListener();
}
+ @Override
@After
public void tearDown() throws Exception {
+ // Wait for the queue to clear...
+ waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS /*ms timeout*/);
mListener.getHandler().removeCallbacksAndMessages(null);
+ mListener = null;
super.tearDown();
}
@@ -86,8 +89,9 @@
}
/**
- * {@link RadioOnStateListener.Callback#isOkToCall(int)} returns true, so we are expecting
- * {@link RadioOnStateListener.Callback#onComplete(boolean)} to return true.
+ * {@link RadioOnStateListener.Callback#isOkToCall(Phone, int)} returns true, so we are
+ * expecting {@link RadioOnStateListener.Callback#onComplete(RadioOnStateListener, boolean)} to
+ * return true.
*/
@Test
@SmallTest
@@ -107,8 +111,9 @@
}
/**
- * We never receive a {@link RadioOnStateListener.Callback#onComplete(boolean)} because
- * {@link RadioOnStateListener.Callback#isOkToCall(int)} returns false.
+ * We never receive a
+ * {@link RadioOnStateListener.Callback#onComplete(RadioOnStateListener, boolean)} because
+ * {@link RadioOnStateListener.Callback#isOkToCall(Phone, int)} returns false.
*/
@Test
@SmallTest
@@ -129,27 +134,27 @@
}
/**
- * Tests {@link RadioOnStateListener.Callback#isOkToCall(int)} returning false and hitting the
- * max number of retries. This should result in
- * {@link RadioOnStateListener.Callback#onComplete(boolean)} returning false.
+ * Tests {@link RadioOnStateListener.Callback#isOkToCall(Phone, int)} returning false and
+ * hitting the max number of retries. This should result in
+ * {@link RadioOnStateListener.Callback#onComplete(RadioOnStateListener, boolean)} returning
+ * false.
*/
@Test
- @FlakyTest
+ @SmallTest
public void testTimeout_RetryFailure() {
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_POWER_OFF);
when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
when(mMockPhone.getServiceState()).thenReturn(state);
when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(false);
- mListener.setTimeBetweenRetriesMillis(50);
+ mListener.setTimeBetweenRetriesMillis(0/*ms*/);
mListener.setMaxNumRetries(2);
// Wait for the timer to expire and check state manually in onRetryTimeout
mListener.waitForRadioOn(mMockPhone, mCallback);
- waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, 500);
+ waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, TIMEOUT_MS /*delay*/);
verify(mCallback).onComplete(eq(mListener), eq(false));
verify(mMockPhone, times(2)).setRadioPower(eq(true));
}
-
}