Merge "Can't dial Emergency call when user turn on airplane mode."
diff --git a/ecc/README.md b/ecc/README.md
index ef795d5..b5dc538 100644
--- a/ecc/README.md
+++ b/ecc/README.md
@@ -6,43 +6,28 @@
gen_eccdata.sh
- A script to launch the newest conversion toolset to convert
- input/eccdata.txt into output/eccdata, and invoke compatibility
- verification tools of earlier versions of toolsets.
+ input/eccdata.txt into output/eccdata.
input/eccdata.txt
- A text file in ProtoBuf text format which contains all known ECC data.
- This file shall be compatible with the last version of format toolset,
- but may not be compatible with all earlier versions.
output/eccdata
- - The binary file generated from input files, with the last version of
- format toolset. This file shall be compatible with all earlier versions.
+ - The binary file generated from input files.
conversion_toolset_v*
- - Contains format definitions, converting tools and verification tools of
- one version of ECC data format.
+ - Contains format definitions and converting tools.
proto
- A symbolic link references to protobuf folder of the newest version of
conversion toolsets. It's used in Android.mk.
-Conversion Toolset
-===================
-
-Every version of conversion toolset shall include at least:
- - A script to generate eccdata from a known version of input/eccdata.txt.
- - A script to verify compatibility of output/eccdata generated by any
- newer version of format toolset.
-
-The output/eccdata generated by a version of conversion toolset shall be
-able to pass compatibility verifications of all previous versions of format
-toolset.
-
Updating ECC database
===================
Steps to update the ECC database:
1. Edit input/eccdata.txt
2. Source and launch
3. Run gen_eccdata.sh
-The database file "output/eccdata" should be updated.
-
+4. Make TeleService
+5. Push TeleService.apk to system/priv-app/TeleService
+6. Reboot device
+7. run 'atest TeleServiceTests:IsoToEccProtobufRepositoryTest#testEccDataContent'
diff --git a/ecc/conversion_toolset_v1/env.sh b/ecc/conversion_toolset_v1/env.sh
index 23d9f10..534c807 100644
--- a/ecc/conversion_toolset_v1/env.sh
+++ b/ecc/conversion_toolset_v1/env.sh
@@ -20,19 +20,3 @@
PROTOBUF_DIR="${LOCAL_TOOLSET_DIR}/proto"
PROTOBUF_FILE="${PROTOBUF_DIR}/protobuf_ecc_data.proto"
RAW_DATA="${INTERMEDIATE_DIR}/eccdata.raw"
-
-read -d "" PYTHON_COMMAND << END || :
-${ANDROID_BUILD_TOP}/prebuilts/python/${KERNEL}-x86/2.7.5/bin/python
-END
-PYTHONPATH="${PYTHONPATH}:${INTERMEDIATE_DIR}"
-PYTHONPATH="${PYTHONPATH}:${ANDROID_BUILD_TOP}/external/nanopb-c/generator/"
-
-if ! [ -x "${PYTHON_COMMAND}" ] ; then
- echo "Missing ${PYTHON_COMMAND}." 1>&2
- exit 1
-fi
-
-"${PROTOC_COMMAND}" \
- --python_out="${INTERMEDIATE_DIR}" \
- --proto_path="${PROTOBUF_DIR}" \
- "${PROTOBUF_FILE}"
diff --git a/ecc/conversion_toolset_v1/gen_eccdata.sh b/ecc/conversion_toolset_v1/gen_eccdata.sh
index 8dd751f..1efac37 100644
--- a/ecc/conversion_toolset_v1/gen_eccdata.sh
+++ b/ecc/conversion_toolset_v1/gen_eccdata.sh
@@ -18,19 +18,12 @@
LOCAL_TOOLSET_DIR="${ECC_ROOT}/conversion_toolset_v1"
source "${LOCAL_TOOLSET_DIR}/env.sh"
+echo "Converting eccdata..."
${ANDROID_BUILD_TOP}/prebuilts/tools/linux-x86_64/protoc/bin/protoc \
--encode=ecc.AllInfo proto/protobuf_ecc_data.proto \
< "${INPUT_DATA}" > "${RAW_DATA}"
-echo
-echo "Starting strict verification"
-"${PYTHON_COMMAND}" -B \
- "${LOCAL_TOOLSET_DIR}/verify_protobuf_compatibility.py" \
- --input="${RAW_DATA}" --strict
-echo "Passed strict verification"
-
-echo
-echo "Compressing and encoding eccdata"
+echo "Compressing eccdata..."
gzip -c < "${RAW_DATA}" > "${OUTPUT_DATA}"
-echo "Done"
+echo "Done"
diff --git a/ecc/conversion_toolset_v1/verify_eccdata_compatibility.sh b/ecc/conversion_toolset_v1/verify_eccdata_compatibility.sh
deleted file mode 100644
index 8686722..0000000
--- a/ecc/conversion_toolset_v1/verify_eccdata_compatibility.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-set -o errexit
-
-# Copyright 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.
-
-LOCAL_TOOLSET_DIR="${ECC_ROOT}/conversion_toolset_v1"
-source "${LOCAL_TOOLSET_DIR}/env.sh"
-
-echo "Starting compatibility verification v1"
-echo "Decoding and decompressing eccdata"
-gunzip -c < "${OUTPUT_DATA}" > "${RAW_DATA}"
-${PYTHON_COMMAND} -B \
- "${LOCAL_TOOLSET_DIR}/verify_protobuf_compatibility.py" \
- --input="${RAW_DATA}"
-echo "Passed compatibility verification v1"
-
diff --git a/ecc/conversion_toolset_v1/verify_protobuf_compatibility.py b/ecc/conversion_toolset_v1/verify_protobuf_compatibility.py
deleted file mode 100644
index bc707eb..0000000
--- a/ecc/conversion_toolset_v1/verify_protobuf_compatibility.py
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/usr/bin/python -B
-
-# Copyright 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.
-
-# Notice:
-# - verify_eccdata_strict.py: Verify data which is generated by this
-# version of this toolset.
-# - verify_eccdata_compatibility.py: Verify data which is generated by any
-# newer version of this tool set for ensuring backward compatibility.
-
-import sys
-import argparse
-import protobuf_ecc_data_pb2
-
-parser = argparse.ArgumentParser()
-parser.add_argument("--input", required=True)
-parser.add_argument("--strict", action="store_true")
-args = parser.parse_args()
-
-all_ecc_info = protobuf_ecc_data_pb2.AllInfo()
-
-with open(args.input, "rb") as ecc_data_source_file:
- all_ecc_info.ParseFromString(ecc_data_source_file.read())
-
-if (args.strict):
- print("Verify in strict mode")
-
-assert all_ecc_info.HasField("revision")
-assert all_ecc_info.revision > 0
-assert len(all_ecc_info.countries) > 0
-
-loaded_iso = []
-for country_info in all_ecc_info.countries:
- assert country_info.HasField("iso_code")
- assert len(country_info.iso_code) > 0
- assert country_info.iso_code == country_info.iso_code.strip().upper()
- assert country_info.iso_code not in loaded_iso
- loaded_iso.append(country_info.iso_code)
- assert country_info.HasField("ecc_fallback")
- assert len(country_info.ecc_fallback) > 0
-
- if len(country_info.eccs) > 0:
- loaded_phone_number = []
- for ecc_info in country_info.eccs:
- assert ecc_info.HasField("phone_number")
- phone_number = ecc_info.phone_number.strip()
- assert len(phone_number) > 0
- assert phone_number not in loaded_phone_number
- loaded_phone_number.append(phone_number)
-
- if (args.strict):
- assert len(ecc_info.types) > 0
- loaded_types = []
- for ecc_type in ecc_info.types:
- assert ecc_type == protobuf_ecc_data_pb2.EccInfo.POLICE or \
- ecc_type == protobuf_ecc_data_pb2.EccInfo.AMBULANCE or \
- ecc_type == protobuf_ecc_data_pb2.EccInfo.FIRE
- assert ecc_type not in loaded_types
- loaded_types.append(ecc_type)
- else:
- # For forward compatibility, ecc_info.types could be null if a phone
- # number contains only new types which is not defined now. Just leave
- # a warning message for this case.
- if len(ecc_info.types) == 0:
- print("WARNING: No recognizable type for " + \
- country_info.iso_code + " - " + ecc_info.phone_number)
- else:
- loaded_types = []
- for ecc_type in ecc_info.types:
- assert ecc_type not in loaded_types
- loaded_types.append(ecc_type)
- elif (args.strict):
- print("Warning: Empty ecc list for country " + country_info.iso_code)
diff --git a/ecc/gen_eccdata.sh b/ecc/gen_eccdata.sh
index 5cf52de..4f3a097 100755
--- a/ecc/gen_eccdata.sh
+++ b/ecc/gen_eccdata.sh
@@ -53,15 +53,8 @@
source "${TOOLSET_DIR}/gen_eccdata.sh"
echo
-# Check compatibility with every previous version
-rm -rf "${INTERMEDIATE_DIR}/*"
-source ${ECC_ROOT}/conversion_toolset_v1/verify_eccdata_compatibility.sh
-#rm -rf "${INTERMEDIATE_DIR}/*"
-#source ${ECC_ROOT}/conversion_toolset_v2/verify_eccdata_compatibility.sh
-#rm -rf "${INTERMEDIATE_DIR}/*"
-#source ${ECC_ROOT}/conversion_toolset_v3/verify_eccdata_compatibility.sh
-#...
-
-echo
-echo "Passed all compatibility verification!"
-
+echo "To verify data compatibility:"
+echo " 1. make TeleService"
+echo " 2. push TeleService.apk to system/priv-app/TeleService"
+echo " 3. reboot device"
+echo " 4. run 'atest TeleServiceTests:IsoToEccProtobufRepositoryTest#testEccDataContent'"
diff --git a/res/values/config.xml b/res/values/config.xml
index 38a0b18..2244fe3 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -235,6 +235,9 @@
<!-- Flag indicating whether the device supports RTT (real-time text) -->
<bool name="config_support_rtt">false</bool>
+ <!-- The package name for the platform number verification supplier app. -->
+ <string name="platform_number_verification_package" translatable="false"></string>
+
<!-- Flag indicating whether a system app can use video calling fallback if carrier video
calling is not available. -->
<bool name="config_support_video_calling_fallback">true</bool>
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index e2f4fee..8d2d71f 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -35,6 +35,7 @@
import android.provider.Settings;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
@@ -303,7 +304,7 @@
if (mImsMgr.isVtEnabledByPlatform() && mImsMgr.isVtProvisionedOnDevice()
&& (carrierConfig.getBoolean(
CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS)
- || mPhone.mDcTracker.isDataEnabled())) {
+ || mPhone.getDcTracker(TransportType.WWAN).isDataEnabled())) {
boolean currentValue =
mImsMgr.isEnhanced4gLteModeSettingEnabledByUser()
? mImsMgr.isVtEnabledByUser() : false;
diff --git a/src/com/android/phone/NumberVerificationManager.java b/src/com/android/phone/NumberVerificationManager.java
new file mode 100644
index 0000000..9ec16f8
--- /dev/null
+++ b/src/com/android/phone/NumberVerificationManager.java
@@ -0,0 +1,192 @@
+/*
+ * 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 android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.telephony.NumberVerificationCallback;
+import android.telephony.PhoneNumberRange;
+import android.telephony.ServiceState;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.INumberVerificationCallback;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+
+/**
+ * Singleton for managing the call based number verification requests.
+ */
+public class NumberVerificationManager {
+ interface PhoneListSupplier {
+ Phone[] getPhones();
+ }
+
+ private static NumberVerificationManager sInstance;
+ private static String sAuthorizedPackageOverride;
+
+ private PhoneNumberRange mCurrentRange;
+ private INumberVerificationCallback mCallback;
+ private final PhoneListSupplier mPhoneListSupplier;
+
+ // We don't really care what thread this runs on, since it's only used for a non-blocking
+ // timeout.
+ private Handler mHandler;
+
+ NumberVerificationManager(PhoneListSupplier phoneListSupplier) {
+ mPhoneListSupplier = phoneListSupplier;
+ mHandler = new Handler(Looper.getMainLooper());
+ }
+
+ private NumberVerificationManager() {
+ this(PhoneFactory::getPhones);
+ }
+
+ /**
+ * Check whether the incoming call matches one of the active filters. If so, call the callback
+ * that says that the number has been successfully verified.
+ * @param number A phone number
+ * @return true if the number matches, false otherwise
+ */
+ public synchronized boolean checkIncomingCall(String number) {
+ if (mCurrentRange == null || mCallback == null) {
+ return false;
+ }
+
+ if (mCurrentRange.matches(number)) {
+ mCurrentRange = null;
+ try {
+ mCallback.onCallReceived(number);
+ return true;
+ } catch (RemoteException e) {
+ Log.w(NumberVerificationManager.class.getSimpleName(),
+ "Remote exception calling verification complete callback");
+ // Intercept the call even if there was a remote exception -- it's still going to be
+ // a strange call from a robot number
+ return true;
+ } finally {
+ mCallback = null;
+ }
+ }
+ return false;
+ }
+
+ synchronized void requestVerification(PhoneNumberRange numberRange,
+ INumberVerificationCallback callback, long timeoutMillis) {
+ if (!checkNumberVerificationFeasibility(callback)) {
+ return;
+ }
+
+ mCallback = callback;
+ mCurrentRange = numberRange;
+
+ mHandler.postDelayed(() -> {
+ synchronized (NumberVerificationManager.this) {
+ // Check whether the verification finished already -- if so, don't call anything.
+ if (mCallback != null && mCurrentRange != null) {
+ try {
+ mCallback.onVerificationFailed(NumberVerificationCallback.REASON_TIMED_OUT);
+ } catch (RemoteException e) {
+ Log.w(NumberVerificationManager.class.getSimpleName(),
+ "Remote exception calling verification error callback");
+ }
+ mCallback = null;
+ mCurrentRange = null;
+ }
+ }
+ }, timeoutMillis);
+ }
+
+ private boolean checkNumberVerificationFeasibility(INumberVerificationCallback callback) {
+ int reason = -1;
+ try {
+ if (mCurrentRange != null || mCallback != null) {
+ reason = NumberVerificationCallback.REASON_CONCURRENT_REQUESTS;
+ return false;
+ }
+ boolean doesAnyPhoneHaveRoomForIncomingCall = false;
+ boolean isAnyPhoneVoiceRegistered = false;
+ for (Phone phone : mPhoneListSupplier.getPhones()) {
+ // abort if any phone is in an emergency call or ecbm
+ if (phone.isInEmergencyCall()) {
+ reason = NumberVerificationCallback.REASON_IN_EMERGENCY_CALL;
+ return false;
+ }
+ if (phone.isInEcm()) {
+ reason = NumberVerificationCallback.REASON_IN_ECBM;
+ return false;
+ }
+
+ // make sure at least one phone is registered for voice
+ if (phone.getServiceState().getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
+ isAnyPhoneVoiceRegistered = true;
+ }
+ // make sure at least one phone has room for an incoming call.
+ if (phone.getRingingCall().getState() == Call.State.IDLE
+ && (phone.getForegroundCall().getState() == Call.State.IDLE
+ || phone.getBackgroundCall().getState() == Call.State.IDLE)) {
+ doesAnyPhoneHaveRoomForIncomingCall = true;
+ }
+ }
+ if (!isAnyPhoneVoiceRegistered) {
+ reason = NumberVerificationCallback.REASON_NETWORK_NOT_AVAILABLE;
+ return false;
+ }
+ if (!doesAnyPhoneHaveRoomForIncomingCall) {
+ reason = NumberVerificationCallback.REASON_TOO_MANY_CALLS;
+ return false;
+ }
+ } finally {
+ if (reason >= 0) {
+ try {
+ callback.onVerificationFailed(reason);
+ } catch (RemoteException e) {
+ Log.w(NumberVerificationManager.class.getSimpleName(),
+ "Remote exception calling verification error callback");
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get the singleton instance of NumberVerificationManager.
+ * @return
+ */
+ public static NumberVerificationManager getInstance() {
+ if (sInstance == null) {
+ sInstance = new NumberVerificationManager();
+ }
+ return sInstance;
+ }
+
+ static String getAuthorizedPackage(Context context) {
+ return !TextUtils.isEmpty(sAuthorizedPackageOverride) ? sAuthorizedPackageOverride :
+ context.getResources().getString(R.string.platform_number_verification_package);
+ }
+
+ /**
+ * Used by shell commands to override the authorized package name for number verification.
+ * @param pkgName
+ */
+ static void overrideAuthorizedPackage(String pkgName) {
+ sAuthorizedPackageOverride = pkgName;
+ }
+}
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 3f679ed..5009bfe 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -46,6 +46,7 @@
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.util.LocalLog;
import android.util.Log;
import android.widget.Toast;
@@ -62,7 +63,6 @@
import com.android.internal.telephony.dataconnection.DataConnectionReasons;
import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.phone.common.CallLogAsync;
import com.android.phone.settings.SettingsConstants;
import com.android.phone.vvm.CarrierVvmPackageInstalledReceiver;
import com.android.services.telephony.sip.SipAccountRegistry;
@@ -713,7 +713,7 @@
}
DataConnectionReasons reasons = new DataConnectionReasons();
- boolean dataAllowed = phone.isDataAllowed(reasons);
+ boolean dataAllowed = phone.isDataAllowed(ApnSetting.TYPE_DEFAULT, reasons);
mDataRoamingNotifLog.log("dataAllowed=" + dataAllowed + ", reasons=" + reasons);
if (VDBG) Log.v(LOG_TAG, "dataAllowed=" + dataAllowed + ", reasons=" + reasons);
if (!mNoDataDueToRoaming
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index dfea90c..912a417 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -16,6 +16,8 @@
package com.android.phone;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import static com.android.internal.telephony.PhoneConstants.SUBSCRIPTION_KEY;
import android.Manifest.permission;
@@ -66,6 +68,7 @@
import android.telephony.ModemActivityInfo;
import android.telephony.NeighboringCellInfo;
import android.telephony.NetworkScanRequest;
+import android.telephony.PhoneNumberRange;
import android.telephony.RadioAccessFamily;
import android.telephony.Rlog;
import android.telephony.ServiceState;
@@ -79,6 +82,7 @@
import android.telephony.UssdResponse;
import android.telephony.VisualVoicemailSmsFilterSettings;
import android.telephony.cdma.CdmaCellLocation;
+import android.telephony.data.ApnSetting;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.gsm.GsmCellLocation;
import android.telephony.ims.aidl.IImsCapabilityCallback;
@@ -105,6 +109,7 @@
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.DefaultPhoneNotifier;
+import com.android.internal.telephony.INumberVerificationCallback;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.LocaleTracker;
@@ -1715,7 +1720,7 @@
try {
final Phone phone = getPhone(subId);
if (phone != null) {
- return phone.isDataAllowed();
+ return phone.isDataAllowed(ApnSetting.TYPE_DEFAULT);
} else {
return false;
}
@@ -2374,6 +2379,30 @@
}
}
+ @Override
+ public void requestNumberVerification(PhoneNumberRange range, long timeoutMillis,
+ INumberVerificationCallback callback, String callingPackage) {
+ if (mApp.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ != PERMISSION_GRANTED) {
+ throw new SecurityException("Caller must hold the MODIFY_PHONE_STATE permission");
+ }
+ mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+
+ String authorizedPackage = NumberVerificationManager.getAuthorizedPackage(mApp);
+ if (!TextUtils.equals(callingPackage, authorizedPackage)) {
+ throw new SecurityException("Calling package must be configured in the device config");
+ }
+
+ if (range == null) {
+ throw new NullPointerException("Range must be non-null");
+ }
+
+ timeoutMillis = Math.min(timeoutMillis,
+ TelephonyManager.MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS);
+
+ NumberVerificationManager.getInstance().requestVerification(range, callback, timeoutMillis);
+ }
+
/**
* Returns true if CDMA provisioning needs to run.
*/
@@ -2599,17 +2628,14 @@
}
/**
- * Returns the unread count of voicemails
- */
- public int getVoiceMessageCount() {
- return getVoiceMessageCountForSubscriber(getDefaultSubscription());
- }
-
- /**
* Returns the unread count of voicemails for a subId
*/
@Override
- public int getVoiceMessageCountForSubscriber( int subId) {
+ public int getVoiceMessageCountForSubscriber(int subId, String callingPackage) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, subId, callingPackage, "getVoiceMessageCountForSubscriber")) {
+ return 0;
+ }
final long identity = Binder.clearCallingIdentity();
try {
final Phone phone = getPhone(subId);
@@ -5339,7 +5365,7 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
if (mPhone.getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
+ != PERMISSION_GRANTED) {
writer.println("Permission Denial: can't dump Phone from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index 1a25ae3..9250118 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -16,6 +16,8 @@
package com.android.phone;
+import android.os.Binder;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.UserHandle;
@@ -41,6 +43,8 @@
private static final String IMS_SUBCOMMAND = "ims";
private static final String SMS_SUBCOMMAND = "sms";
+ private static final String NUMBER_VERIFICATION_SUBCOMMAND = "numverify";
+
private static final String IMS_SET_CARRIER_SERVICE = "set-ims-service";
private static final String IMS_GET_CARRIER_SERVICE = "get-ims-service";
private static final String IMS_ENABLE = "enable";
@@ -50,6 +54,9 @@
private static final String SMS_GET_DEFAULT_APP = "get-default-app";
private static final String SMS_SET_DEFAULT_APP = "set-default-app";
+ private static final String NUMBER_VERIFICATION_OVERRIDE_PACKAGE = "override-package";
+ private static final String NUMBER_VERIFICATION_FAKE_CALL = "fake-call";
+
// Take advantage of existing methods that already contain permissions checks when possible.
private final ITelephony mInterface;
@@ -70,6 +77,8 @@
case SMS_SUBCOMMAND: {
return handleSmsCommand();
}
+ case NUMBER_VERIFICATION_SUBCOMMAND:
+ return handleNumberVerificationCommand();
default: {
return handleDefaultCommands(cmd);
}
@@ -126,6 +135,18 @@
pw.println(" Set PACKAGE_NAME as the default SMS app.");
}
+
+ private void onHelpNumberVerification() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Number verification commands");
+ pw.println(" numverify override-package PACKAGE_NAME;");
+ pw.println(" Set the authorized package for number verification.");
+ pw.println(" Leave the package name blank to reset.");
+ pw.println(" numverify fake-call NUMBER;");
+ pw.println(" Fake an incoming call from NUMBER. This is for testing. Output will be");
+ pw.println(" 1 if the call would have been intercepted, 0 otherwise.");
+ }
+
private int handleImsCommand() {
String arg = getNextArg();
if (arg == null) {
@@ -151,6 +172,33 @@
return -1;
}
+ private int handleNumberVerificationCommand() {
+ String arg = getNextArg();
+ if (arg == null) {
+ onHelpNumberVerification();
+ return 0;
+ }
+
+ if (!checkShellUid()) {
+ return -1;
+ }
+
+ switch (arg) {
+ case NUMBER_VERIFICATION_OVERRIDE_PACKAGE: {
+ NumberVerificationManager.overrideAuthorizedPackage(getNextArg());
+ return 0;
+ }
+ case NUMBER_VERIFICATION_FAKE_CALL: {
+ boolean val = NumberVerificationManager.getInstance()
+ .checkIncomingCall(getNextArg());
+ getOutPrintWriter().println(val ? "1" : "0");
+ return 0;
+ }
+ }
+
+ return -1;
+ }
+
// ims set-ims-service
private int handleImsSetServiceCommand() {
PrintWriter errPw = getErrPrintWriter();
@@ -400,4 +448,10 @@
getOutPrintWriter().println("SMS app set to " + mInterface.getDefaultSmsApp(userId));
return 0;
}
+
+ private boolean checkShellUid() {
+ // adb can run as root or as shell, depending on whether the device is rooted.
+ return Binder.getCallingUid() == Process.SHELL_UID
+ || Binder.getCallingUid() == Process.ROOT_UID;
+ }
}
diff --git a/src/com/android/phone/ecc/IsoToEccProtobufRepository.java b/src/com/android/phone/ecc/IsoToEccProtobufRepository.java
index 0cd3108..7d9b4f0 100644
--- a/src/com/android/phone/ecc/IsoToEccProtobufRepository.java
+++ b/src/com/android/phone/ecc/IsoToEccProtobufRepository.java
@@ -25,6 +25,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import java.io.BufferedInputStream;
import java.io.IOException;
@@ -109,6 +110,11 @@
}
}
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ Map<String, CountryEccInfo> getEccTable() {
+ return mEccTable;
+ }
+
private ProtobufEccData.AllInfo parseEccData(InputStream input) throws IOException {
return ProtobufEccData.AllInfo.parseFrom(new GZIPInputStream(input));
}
diff --git a/src/com/android/services/telephony/PstnIncomingCallNotifier.java b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
index 4dfaf44..223616f 100644
--- a/src/com/android/services/telephony/PstnIncomingCallNotifier.java
+++ b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
@@ -37,6 +37,7 @@
import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
import com.android.internal.telephony.imsphone.ImsExternalConnection;
import com.android.internal.telephony.imsphone.ImsPhoneConnection;
+import com.android.phone.NumberVerificationManager;
import com.android.phone.PhoneUtils;
import com.google.common.base.Preconditions;
@@ -126,6 +127,19 @@
Connection connection = (Connection) asyncResult.result;
if (connection != null) {
Call call = connection.getCall();
+ // Check if we have a pending number verification request.
+ if (connection.getAddress() != null) {
+ if (NumberVerificationManager.getInstance()
+ .checkIncomingCall(connection.getAddress())) {
+ // Disconnect the call if it matches
+ try {
+ connection.hangup();
+ } catch (CallStateException e) {
+ Log.e(this, e, "Error hanging up potential number verification call");
+ }
+ return;
+ }
+ }
// Final verification of the ringing state before sending the intent to Telecom.
if (call != null && call.getState().isRinging()) {
diff --git a/testapps/TelephonyManagerTestApp/res/layout/calling_method.xml b/testapps/TelephonyManagerTestApp/res/layout/calling_method.xml
index 668b708..5145a63 100644
--- a/testapps/TelephonyManagerTestApp/res/layout/calling_method.xml
+++ b/testapps/TelephonyManagerTestApp/res/layout/calling_method.xml
@@ -58,11 +58,12 @@
android:ellipsize="marquee"
android:textSize="30sp" />
- <ListView
- android:id="@android:id/list"
+ <LinearLayout
+ android:id="@+id/method_params"
+ android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_width="fill_parent">
- </ListView>
+ </LinearLayout>
<Button
android:id="@+id/go_button"
@@ -72,10 +73,18 @@
android:layout_height="50dip">
</Button>
- <TextView
- android:id="@+id/return_value"
+
+ <ScrollView
+ android:id="@+id/return_value_wrapper"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:ellipsize="marquee"
- android:textSize="15sp" />
+ android:scrollbars="vertical"
+ android:fillViewport="true">
+ <TextView
+ android:id="@+id/return_value"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:ellipsize="marquee"
+ android:textSize="15sp" />
+ </ScrollView>
</LinearLayout>
diff --git a/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/CallingMethodActivity.java b/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/CallingMethodActivity.java
index aa9dbc0..4bf8220 100644
--- a/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/CallingMethodActivity.java
+++ b/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/CallingMethodActivity.java
@@ -16,27 +16,28 @@
package com.android.phone.testapps.telephonymanagertestapp;
-import android.app.ListActivity;
+import android.app.Activity;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.LinearLayout;
import android.widget.TextView;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-import java.util.ArrayList;
/**
* Activity to call a specific method of TelephonyManager.
*/
-public class CallingMethodActivity extends ListActivity {
+public class CallingMethodActivity extends Activity {
private Class[] mParameterTypes;
private Object[] mParameterValues;
+ EditText[] mEditTexts;
private Button mGoButton;
private Method mMethod;
private TextView mReturnValue;
@@ -57,10 +58,11 @@
mGoButton = findViewById(R.id.go_button);
mReturnValue = findViewById(R.id.return_value);
mSubIdField = findViewById(R.id.sub_id_value);
- setListAdapter(new ParameterListAdapter());
mParameterTypes = mMethod.getParameterTypes();
mParameterValues = new Object[mParameterTypes.length];
+ mEditTexts = new EditText[mParameterTypes.length];
+ populateParamList();
String tags = Modifier.toString(mMethod.getModifiers()) + ' '
+ TelephonyManagerTestApp.getShortTypeName(mMethod.getReturnType().toString());
@@ -76,13 +78,18 @@
int subId = Integer.parseInt(mSubIdField.getText().toString());
for (int i = 0; i < mParameterTypes.length; i++) {
- String text = ((EditText) getListAdapter().getItem(i)).getText().toString();
+ String text = mEditTexts[i].getText().toString();
if (mParameterTypes[i] == int.class) {
mParameterValues[i] = Integer.parseInt(text);
} else if (mParameterTypes[i] == boolean.class) {
mParameterValues[i] = Boolean.parseBoolean(text);
- } else if (mParameterTypes[i] == Long.class) {
+ } else if (mParameterTypes[i] == long.class) {
mParameterValues[i] = Long.parseLong(text);
+ } else if (mParameterTypes[i] == String.class) {
+ mParameterValues[i] = text;
+ } else {
+ mParameterValues[i] =
+ ParameterParser.get(this).executeParser(mParameterTypes[i], text);
}
}
Log.d(TelephonyManagerTestApp.TAG, "Invoking method " + mMethod.getName());
@@ -103,50 +110,22 @@
}
} catch (Exception exception) {
- Log.d(TelephonyManagerTestApp.TAG, "NoSuchMethodException " + exception);
- mReturnValue.setText("NoSuchMethodException " + exception);
+ Log.d(TelephonyManagerTestApp.TAG, "Exception: " + exception);
+ StringWriter s = new StringWriter();
+ PrintWriter stack = new PrintWriter(s);
+ exception.printStackTrace(stack);
+ mReturnValue.setText("Exception " + exception.getMessage() + "\n" + s.toString());
}
}
- private class ParameterListAdapter extends BaseAdapter {
- ArrayList<EditText> mEditTexts = new ArrayList<>();
- @Override
- public int getCount() {
- return mParameterTypes == null ? 0 : mParameterTypes.length;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup container) {
- if (mParameterTypes == null || mParameterTypes.length <= position) {
- return null;
- }
-
- if (convertView == null) {
- convertView = getLayoutInflater().inflate(
- R.layout.parameter_field, container, false);
- }
-
- Class aClass = mParameterTypes[position];
-
- ((TextView) convertView.findViewById(R.id.field_name)).setText(
+ private void populateParamList() {
+ for (int i = 0; i < mParameterTypes.length; i++) {
+ View view = getLayoutInflater().inflate(R.layout.parameter_field, null);
+ Class aClass = mParameterTypes[i];
+ ((TextView) view.findViewById(R.id.field_name)).setText(
TelephonyManagerTestApp.getShortTypeName(aClass.toString()) + ": ");
- mEditTexts.add(convertView.findViewById(R.id.field_value));
-
- return convertView;
- }
-
- @Override
- public Object getItem(int position) {
- if (mEditTexts == null || mEditTexts.size() <= position) {
- return null;
- }
-
- return mEditTexts.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
+ mEditTexts[i] = view.findViewById(R.id.field_value);
+ ((LinearLayout) findViewById(R.id.method_params)).addView(view);
}
}
}
diff --git a/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/ParameterParser.java b/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/ParameterParser.java
new file mode 100644
index 0000000..097c90a
--- /dev/null
+++ b/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/ParameterParser.java
@@ -0,0 +1,82 @@
+/*
+ * 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.testapps.telephonymanagertestapp;
+
+import android.content.Context;
+import android.telephony.NumberVerificationCallback;
+import android.telephony.PhoneNumberRange;
+import android.widget.Toast;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.function.Function;
+
+class ParameterParser {
+ private static ParameterParser sInstance;
+
+ static ParameterParser get(Context context) {
+ if (sInstance == null) {
+ sInstance = new ParameterParser(context);
+ }
+ return sInstance;
+ }
+
+ private final Context mContext;
+ private final Map<Class, Function<String, Object>> mParsers =
+ new HashMap<Class, Function<String, Object>>() {{
+ put(PhoneNumberRange.class, ParameterParser::parsePhoneNumberRange);
+ put(Executor.class, s -> parseExecutor(s));
+ put(NumberVerificationCallback.class, s -> parseNumberVerificationCallback(s));
+ }};
+
+ private ParameterParser(Context context) {
+ mContext = context;
+ }
+
+ Object executeParser(Class type, String input) {
+ return mParsers.getOrDefault(type, s -> null).apply(input);
+ }
+
+ private static PhoneNumberRange parsePhoneNumberRange(String input) {
+ String[] parts = input.split(" ");
+ if (parts.length != 4) {
+ return null;
+ }
+ return new PhoneNumberRange(parts[0], parts[1], parts[2], parts[3]);
+ }
+
+ private Executor parseExecutor(String input) {
+ return mContext.getMainExecutor();
+ }
+
+ private NumberVerificationCallback parseNumberVerificationCallback(String input) {
+ return new NumberVerificationCallback() {
+ @Override
+ public void onCallReceived(String phoneNumber) {
+ Toast.makeText(mContext, "Received verification " + phoneNumber,
+ Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onVerificationFailed(int reason) {
+ Toast.makeText(mContext, "Verification failed " + reason,
+ Toast.LENGTH_SHORT).show();
+ }
+ };
+ }
+}
diff --git a/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/TelephonyManagerTestApp.java b/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/TelephonyManagerTestApp.java
index 45c76a7..760c3bd 100644
--- a/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/TelephonyManagerTestApp.java
+++ b/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/TelephonyManagerTestApp.java
@@ -158,7 +158,7 @@
mFilteredMethods.addAll(mMethods);
} else {
for (Method method : mMethods) {
- if (method.getName().contains(text)) {
+ if (method.getName().toLowerCase().contains(text.toLowerCase())) {
mFilteredMethods.add(method);
}
}
diff --git a/tests/src/com/android/phone/NumberVerificationManagerTest.java b/tests/src/com/android/phone/NumberVerificationManagerTest.java
new file mode 100644
index 0000000..d476ba5
--- /dev/null
+++ b/tests/src/com/android/phone/NumberVerificationManagerTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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 junit.framework.TestCase.assertFalse;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+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.telephony.NumberVerificationCallback;
+import android.telephony.PhoneNumberRange;
+import android.telephony.ServiceState;
+
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.INumberVerificationCallback;
+import com.android.internal.telephony.Phone;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(JUnit4.class)
+public class NumberVerificationManagerTest {
+ private static final PhoneNumberRange SAMPLE_RANGE =
+ new PhoneNumberRange("1", "650555", "0000", "8999");
+ private static final long DEFAULT_VERIFICATION_TIMEOUT = 100;
+ @Mock private Phone mPhone1;
+ @Mock private Phone mPhone2;
+ @Mock private Call mRingingCall;
+ @Mock private Call mForegroundCall;
+ @Mock private Call mBackgroundCall;
+ @Mock private INumberVerificationCallback mCallback;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ServiceState ss = mock(ServiceState.class);
+ when(ss.getVoiceRegState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+ when(mPhone1.getServiceState()).thenReturn(ss);
+ when(mPhone1.getForegroundCall()).thenReturn(mForegroundCall);
+ when(mPhone1.getRingingCall()).thenReturn(mRingingCall);
+ when(mPhone1.getBackgroundCall()).thenReturn(mBackgroundCall);
+ when(mPhone2.getServiceState()).thenReturn(ss);
+ when(mPhone2.getForegroundCall()).thenReturn(mForegroundCall);
+ when(mPhone2.getRingingCall()).thenReturn(mRingingCall);
+ when(mPhone2.getBackgroundCall()).thenReturn(mBackgroundCall);
+
+ when(mForegroundCall.getState()).thenReturn(Call.State.IDLE);
+ when(mRingingCall.getState()).thenReturn(Call.State.IDLE);
+ when(mBackgroundCall.getState()).thenReturn(Call.State.IDLE);
+ }
+
+ @Test
+ public void testConcurrentRequestFailure() throws Exception {
+ NumberVerificationManager manager =
+ new NumberVerificationManager(() -> new Phone[]{mPhone1});
+ manager.requestVerification(SAMPLE_RANGE, mCallback, DEFAULT_VERIFICATION_TIMEOUT);
+ manager.requestVerification(SAMPLE_RANGE, mCallback, DEFAULT_VERIFICATION_TIMEOUT);
+ verify(mCallback, times(1)).onVerificationFailed(
+ NumberVerificationCallback.REASON_CONCURRENT_REQUESTS);
+ }
+
+ @Test
+ public void testEcbmFailure() throws Exception {
+ NumberVerificationManager manager =
+ new NumberVerificationManager(() -> new Phone[]{mPhone1});
+ when(mPhone1.isInEcm()).thenReturn(true);
+
+ manager.requestVerification(SAMPLE_RANGE, mCallback, DEFAULT_VERIFICATION_TIMEOUT);
+ verify(mCallback, times(1)).onVerificationFailed(
+ NumberVerificationCallback.REASON_IN_ECBM);
+ }
+
+ @Test
+ public void testEmergencyCallFailure() throws Exception {
+ NumberVerificationManager manager =
+ new NumberVerificationManager(() -> new Phone[]{mPhone1});
+ when(mPhone1.isInEmergencyCall()).thenReturn(true);
+
+ manager.requestVerification(SAMPLE_RANGE, mCallback, DEFAULT_VERIFICATION_TIMEOUT);
+ verify(mCallback, times(1)).onVerificationFailed(
+ NumberVerificationCallback.REASON_IN_EMERGENCY_CALL);
+ }
+
+ @Test
+ public void testNoPhoneInServiceFailure() throws Exception {
+ ServiceState ss = mock(ServiceState.class);
+ when(ss.getVoiceRegState()).thenReturn(ServiceState.STATE_POWER_OFF);
+ when(mPhone1.getServiceState()).thenReturn(ss);
+ when(mPhone2.getServiceState()).thenReturn(ss);
+ NumberVerificationManager manager =
+ new NumberVerificationManager(() -> new Phone[]{mPhone1, mPhone2});
+
+ manager.requestVerification(SAMPLE_RANGE, mCallback, DEFAULT_VERIFICATION_TIMEOUT);
+ verify(mCallback, times(1)).onVerificationFailed(
+ NumberVerificationCallback.REASON_NETWORK_NOT_AVAILABLE);
+ }
+
+ @Test
+ public void testAllLinesFullFailure() throws Exception {
+ NumberVerificationManager manager =
+ new NumberVerificationManager(() -> new Phone[]{mPhone1, mPhone2});
+ when(mRingingCall.getState()).thenReturn(Call.State.ALERTING);
+
+ manager.requestVerification(SAMPLE_RANGE, mCallback, DEFAULT_VERIFICATION_TIMEOUT);
+ verify(mCallback, times(1)).onVerificationFailed(
+ NumberVerificationCallback.REASON_TOO_MANY_CALLS);
+ }
+
+ private void verifyDefaultRangeMatching(NumberVerificationManager manager) throws Exception {
+ String testNumber = "6505550000";
+ assertTrue(manager.checkIncomingCall(testNumber));
+ verify(mCallback).onCallReceived(testNumber);
+ }
+
+ @Test
+ public void testVerificationWorksWithOnePhoneInService() throws Exception {
+ ServiceState ss = mock(ServiceState.class);
+ when(ss.getVoiceRegState()).thenReturn(ServiceState.STATE_POWER_OFF);
+ when(mPhone1.getServiceState()).thenReturn(ss);
+ NumberVerificationManager manager =
+ new NumberVerificationManager(() -> new Phone[]{mPhone1, mPhone2});
+
+ manager.requestVerification(SAMPLE_RANGE, mCallback, DEFAULT_VERIFICATION_TIMEOUT);
+ verify(mCallback, never()).onVerificationFailed(anyInt());
+ verifyDefaultRangeMatching(manager);
+ }
+
+ @Test
+ public void testVerificationWorksWithOnePhoneFull() throws Exception {
+ Call fakeCall = mock(Call.class);
+ when(fakeCall.getState()).thenReturn(Call.State.ACTIVE);
+ when(mPhone1.getForegroundCall()).thenReturn(fakeCall);
+ when(mPhone1.getRingingCall()).thenReturn(fakeCall);
+ when(mPhone1.getBackgroundCall()).thenReturn(fakeCall);
+ NumberVerificationManager manager =
+ new NumberVerificationManager(() -> new Phone[]{mPhone1, mPhone2});
+
+ manager.requestVerification(SAMPLE_RANGE, mCallback, DEFAULT_VERIFICATION_TIMEOUT);
+ verify(mCallback, never()).onVerificationFailed(anyInt());
+ verifyDefaultRangeMatching(manager);
+ }
+
+ @Test
+ public void testDoubleVerificationFailure() throws Exception {
+ NumberVerificationManager manager =
+ new NumberVerificationManager(() -> new Phone[]{mPhone1, mPhone2});
+ manager.requestVerification(SAMPLE_RANGE, mCallback, DEFAULT_VERIFICATION_TIMEOUT);
+ verifyDefaultRangeMatching(manager);
+ assertFalse(manager.checkIncomingCall("this doesn't even matter"));
+ }
+}
diff --git a/tests/src/com/android/phone/ecc/IsoToEccProtobufRepositoryTest.java b/tests/src/com/android/phone/ecc/IsoToEccProtobufRepositoryTest.java
new file mode 100644
index 0000000..f6e5ba2
--- /dev/null
+++ b/tests/src/com/android/phone/ecc/IsoToEccProtobufRepositoryTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.ecc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import com.android.TelephonyTestBase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+import java.util.Map;
+
+/**
+ * Unit tests for IsoToEccProtobufRepository.
+ */
+
+@RunWith(AndroidJUnit4.class)
+public class IsoToEccProtobufRepositoryTest extends TelephonyTestBase {
+ private static final String LOG_TAG = "IsoToEccProtobufRepositoryTest";
+
+ @Test
+ public void testEccDataContent() {
+ IsoToEccProtobufRepository repository = IsoToEccProtobufRepository.getInstance();
+ repository.loadMappingTable(InstrumentationRegistry.getTargetContext());
+ Map<String, CountryEccInfo> eccTable = repository.getEccTable();
+ HashSet loadedIsos = new HashSet(300);
+ HashSet loadedNumbers = new HashSet(5);
+
+ assertThat(eccTable).isNotEmpty();
+ for (Map.Entry<String, CountryEccInfo> entry : eccTable.entrySet()) {
+ String countryIso = entry.getKey();
+ CountryEccInfo countryEccInfo = entry.getValue();
+ EccInfo[] eccInfoList = countryEccInfo.getEccInfoList();
+ if (eccInfoList.length > 0) {
+ Log.i(LOG_TAG, "Verifying country " + countryIso + " with "
+ + eccInfoList.length + " ecc(s)");
+ } else {
+ Log.w(LOG_TAG, "Verifying country " + countryIso + " with no ecc");
+ }
+
+ assertThat(countryIso).isNotEmpty();
+ assertThat(countryIso).isEqualTo(countryIso.toUpperCase().trim());
+ assertThat(loadedIsos.contains(countryIso)).isFalse();
+ loadedIsos.add(countryIso);
+
+ assertThat(countryEccInfo.getFallbackEcc()).isNotEmpty();
+
+ if (eccInfoList.length != 0) {
+ loadedNumbers.clear();
+ for (EccInfo eccInfo : eccInfoList) {
+ String eccNumber = eccInfo.getNumber();
+ assertThat(eccNumber).isNotEmpty();
+ assertThat(eccNumber).isEqualTo(eccNumber.trim());
+ assertThat(eccInfo.getTypes()).isNotEmpty();
+ assertThat(loadedNumbers.contains(eccNumber)).isFalse();
+ loadedNumbers.add(eccNumber);
+ }
+ }
+ }
+ }
+}