Merge "Fix read/write permission error in ProvisioningManager"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 30760dc..ce774d8 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -65,6 +65,7 @@
<protected-broadcast android:name= "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE" />
<protected-broadcast android:name= "com.android.internal.telephony.CARRIER_SIGNAL_RESET" />
<protected-broadcast android:name= "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE" />
+ <protected-broadcast android:name= "com.android.internal.telephony.ACTION_LINE1_NUMBER_ERROR_DETECTED" />
<protected-broadcast android:name= "com.android.internal.provider.action.VOICEMAIL_SMS_RECEIVED" />
<protected-broadcast android:name= "com.android.intent.isim_refresh" />
<protected-broadcast android:name= "com.android.ims.ACTION_RCS_SERVICE_AVAILABLE" />
@@ -90,6 +91,7 @@
<protected-broadcast android:name= "android.telephony.action.SIM_APPLICATION_STATE_CHANGED" />
<protected-broadcast android:name= "android.telephony.action.SIM_SLOT_STATUS_CHANGED" />
<protected-broadcast android:name= "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED" />
+ <protected-broadcast android:name= "android.telephony.action.SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.CALL_PHONE" />
@@ -214,7 +216,8 @@
<activity android:name="EmergencyDialer"
android:label="@string/emergencyDialerIconLabel"
android:theme="@style/EmergencyDialerTheme"
- android:screenOrientation="portrait">
+ android:screenOrientation="portrait"
+ android:resizeableActivity="false">
<intent-filter>
<action android:name="com.android.phone.EmergencyDialer.DIAL" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/ecc/input/eccdata.txt b/ecc/input/eccdata.txt
index 54be16b..403cad7 100644
--- a/ecc/input/eccdata.txt
+++ b/ecc/input/eccdata.txt
@@ -808,7 +808,7 @@
countries {
iso_code: "GB"
eccs {
- phone_number: "112"
+ phone_number: "999"
types: POLICE
types: AMBULANCE
types: FIRE
diff --git a/ecc/output/eccdata b/ecc/output/eccdata
index 2d0165a..679bc36 100644
--- a/ecc/output/eccdata
+++ b/ecc/output/eccdata
Binary files differ
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7fe534c..d6c5fd7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1804,7 +1804,11 @@
<!-- Message displayed to the user to indicate that a held call has been released /
disconnected. -->
<string name="supp_service_held_call_released">Held call has been released.</string>
-
+ <!-- In-call screen: error message shown when the user attempts to place a call, but the device
+ is currently in the process of being provisioned for service. Provisioning is the process
+ by which a device confirms which services are available to the user by contacting the
+ mobile service provider. -->
+ <string name="callFailed_otasp_provisioning_in_process">Cannot place a call as the device is currently being provisioned.</string>
<!-- In-call screen: error message shown when the user has attempted to place a new outgoing
call, but there is already a call in dialing state. -->
<string name="callFailed_already_dialing">Cannot place a call as another outgoing call is already dialing.</string>
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index bddfd5f..8194c60 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -70,7 +70,6 @@
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.PrintWriter;
-import java.lang.Math;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -221,10 +220,8 @@
case EVENT_DO_FETCH_DEFAULT:
{
- final String iccid = getIccIdForPhoneId(phoneId);
- final String gid1 = getGid1ForPhoneId(phoneId);
final PersistableBundle config =
- restoreConfigFromXml(mPlatformCarrierConfigPackage, iccid, gid1);
+ restoreConfigFromXml(mPlatformCarrierConfigPackage, phoneId);
if (config != null) {
log(
"Loaded config from XML. package="
@@ -264,8 +261,6 @@
break;
}
final CarrierIdentifier carrierId = getCarrierIdForPhoneId(phoneId);
- final String iccid = getIccIdForPhoneId(phoneId);
- final String gid1 = getGid1ForPhoneId(phoneId);
// ResultReceiver callback will execute in this Handler's thread.
final ResultReceiver resultReceiver =
new ResultReceiver(this) {
@@ -286,8 +281,8 @@
}
PersistableBundle config =
resultData.getParcelable(KEY_CONFIG_BUNDLE);
- saveConfigToXml(mPlatformCarrierConfigPackage,
- iccid, gid1, config);
+ saveConfigToXml(
+ mPlatformCarrierConfigPackage, phoneId, config);
mConfigFromDefaultApp[phoneId] = config;
sendMessage(
obtainMessage(
@@ -342,10 +337,8 @@
case EVENT_DO_FETCH_CARRIER:
{
final String carrierPackageName = getCarrierPackageForPhoneId(phoneId);
- final String iccid = getIccIdForPhoneId(phoneId);
- final String gid1 = getGid1ForPhoneId(phoneId);
final PersistableBundle config =
- restoreConfigFromXml(carrierPackageName, iccid, gid1);
+ restoreConfigFromXml(carrierPackageName, phoneId);
if (config != null) {
log(
"Loaded config from XML. package="
@@ -384,8 +377,6 @@
break;
}
final CarrierIdentifier carrierId = getCarrierIdForPhoneId(phoneId);
- final String iccid = getIccIdForPhoneId(phoneId);
- final String gid1 = getGid1ForPhoneId(phoneId);
// ResultReceiver callback will execute in this Handler's thread.
final ResultReceiver resultReceiver =
new ResultReceiver(this) {
@@ -406,8 +397,8 @@
}
PersistableBundle config =
resultData.getParcelable(KEY_CONFIG_BUNDLE);
- saveConfigToXml(getCarrierPackageForPhoneId(phoneId),
- iccid, gid1, config);
+ saveConfigToXml(
+ getCarrierPackageForPhoneId(phoneId), phoneId, config);
mConfigFromCarrierApp[phoneId] = config;
sendMessage(
obtainMessage(
@@ -635,13 +626,19 @@
* In case of errors or invalid input, no file will be written.
*
* @param packageName the name of the package from which we fetched this bundle.
- * @param iccid the ICCID of the subscription for which this bundle was fetched.
- * @param extras First 20 characters of gid1 of the subscription for which the bundle
- * was fetched.
+ * @param phoneId the phone ID.
* @param config the bundle to be written. Null will be treated as an empty bundle.
*/
- private void saveConfigToXml(String packageName, String iccid, String extras,
- PersistableBundle config) {
+ private void saveConfigToXml(String packageName, int phoneId, PersistableBundle config) {
+ if (SubscriptionManager.getSimStateForSlotIndex(phoneId)
+ != TelephonyManager.SIM_STATE_LOADED) {
+ log("Skip save config because SIM records are not loaded.");
+ return;
+ }
+
+ final String iccid = getIccIdForPhoneId(phoneId);
+ // extras is the first 20 characters of gid1
+ final String extras = getGid1ForPhoneId(phoneId);
if (packageName == null || iccid == null) {
loge("Cannot save config with null packageName or iccid.");
return;
@@ -693,25 +690,31 @@
* Reads a bundle from an XML file.
*
* This restores a bundle that was written with {@link #saveConfigToXml}. This returns the saved
- * config bundle for the given package and ICCID.
+ * config bundle for the given package and phone ID.
*
* In case of errors, or if the saved config is from a different package version than the
* current version, then null will be returned.
*
* @param packageName the name of the package from which we fetched this bundle.
- * @param iccid the ICCID of the subscription for which this bundle was fetched.
- * @param extras First 20 characters of gid1 of the subscription for which the bundle
- * was fetched.
+ * @param phoneId the phone ID.
* @return the bundle from the XML file. Returns null if there is no saved config, the saved
* version does not match, or reading config fails.
*/
- private PersistableBundle restoreConfigFromXml(String packageName, String iccid,
- String extras) {
+ private PersistableBundle restoreConfigFromXml(String packageName, int phoneId) {
final String version = getPackageVersion(packageName);
if (version == null) {
loge("Failed to get package version for: " + packageName);
return null;
}
+ if (SubscriptionManager.getSimStateForSlotIndex(phoneId)
+ != TelephonyManager.SIM_STATE_LOADED) {
+ log("Skip restoring config because SIM records are not yet loaded.");
+ return null;
+ }
+
+ final String iccid = getIccIdForPhoneId(phoneId);
+ // extras is the first 20 characters of gid1
+ final String extras = getGid1ForPhoneId(phoneId);
if (packageName == null || iccid == null) {
loge("Cannot restore config with null packageName or iccid.");
return null;
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index 1619aa1..47455c5 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -357,11 +357,16 @@
CarrierConfigManager.KEY_SUPPORT_EMERGENCY_DIALER_SHORTCUT_BOOL);
Log.d(LOG_TAG, "Is the carrier supported: " + isSupport);
TelephonyManager tm = getSystemService(TelephonyManager.class);
- if (isSupport && !TextUtils.isEmpty(tm.getNetworkCountryIso())) {
- mAreEmergencyDialerShortcutsEnabled = true;
- } else {
- mAreEmergencyDialerShortcutsEnabled = false;
+ String countryIso = tm.getNetworkCountryIso();
+ mAreEmergencyDialerShortcutsEnabled = false;
+ if (isSupport && !TextUtils.isEmpty(countryIso)) {
+ if (EccInfoHelper.isCountryEccInfoAvailable(this, countryIso)) {
+ mAreEmergencyDialerShortcutsEnabled = true;
+ } else {
+ Log.d(LOG_TAG, "ECC info is unavailable. Disable emergency dialer shortcut.");
+ }
}
+ mAreEmergencyDialerShortcutsEnabled = false;
Log.d(LOG_TAG, "Enable emergency dialer shortcut: "
+ mAreEmergencyDialerShortcutsEnabled);
@@ -458,7 +463,7 @@
mEmergencyInfoGroup = (EmergencyInfoGroup) findViewById(R.id.emergency_info_button);
if (mAreEmergencyDialerShortcutsEnabled) {
- mEccInfoHelper = new EccInfoHelper(new IsoToEccProtobufRepository());
+ mEccInfoHelper = new EccInfoHelper(IsoToEccProtobufRepository.getInstance());
setupEmergencyShortcutsView();
}
}
@@ -928,7 +933,7 @@
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int ringerMode = audioManager.getRingerMode();
if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
- || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
+ || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
return;
}
@@ -1054,8 +1059,8 @@
boolean isCellAvailable =
ss.getRilVoiceRadioTechnology() != RIL_RADIO_TECHNOLOGY_UNKNOWN;
Log.i(LOG_TAG, "showWfcWarningTask: isWfcAvailable=" + isWfcAvailable
- + " isCellAvailable=" + isCellAvailable
- + "(rat=" + ss.getRilVoiceRadioTechnology() + ")");
+ + " isCellAvailable=" + isCellAvailable
+ + "(rat=" + ss.getRilVoiceRadioTechnology() + ")");
return isWfcAvailable && !isCellAvailable;
}
diff --git a/src/com/android/phone/NetworkSelectSetting.java b/src/com/android/phone/NetworkSelectSetting.java
index 248ecc1..9b905b4 100644
--- a/src/com/android/phone/NetworkSelectSetting.java
+++ b/src/com/android/phone/NetworkSelectSetting.java
@@ -417,7 +417,6 @@
mConnectedNetworkOperatorsPreference.addPreference(pref);
} else {
- loge("Invalid CellIfno: " + cellInfo.toString());
// Remove the connected network operators category
removeConnectedNetworkOperatorPreference();
}
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index 5aa87d0..de3fffc 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -30,7 +30,10 @@
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
import android.os.PersistableBundle;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -50,6 +53,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.SparseArray;
import android.widget.Toast;
import com.android.internal.telephony.Phone;
@@ -89,6 +93,14 @@
static final int DATA_DISCONNECTED_ROAMING_NOTIFICATION = 5;
static final int SELECTED_OPERATOR_FAIL_NOTIFICATION = 6;
+ // Event for network selection notification.
+ private static final int EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION = 1;
+
+ private static final long NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS = 10000L;
+ private static final int NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES = 10;
+
+ private static final int STATE_UNKNOWN_SERVICE = -1;
+
/** The singleton NotificationMgr instance. */
private static NotificationMgr sInstance;
@@ -103,12 +115,36 @@
private TelecomManager mTelecomManager;
private TelephonyManager mTelephonyManager;
- // used to track the notification of selected network unavailable
- private boolean mSelectedUnavailableNotify = false;
+ // used to track the notification of selected network unavailable, per subscription id.
+ private SparseArray<Boolean> mSelectedUnavailableNotify = new SparseArray<>();
// used to track whether the message waiting indicator is visible, per subscription id.
private ArrayMap<Integer, Boolean> mMwiVisible = new ArrayMap<Integer, Boolean>();
+ // those flags are used to track whether to show network selection notification or not.
+ private SparseArray<Integer> mPreviousServiceState = new SparseArray<>();
+ private SparseArray<Long> mOOSTimestamp = new SparseArray<>();
+ private SparseArray<Integer> mPendingEventCounter = new SparseArray<>();
+ // maps each subId to selected network operator name.
+ private SparseArray<String> mSelectedNetworkOperatorName = new SparseArray<>();
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION:
+ int subId = (int) msg.obj;
+ TelephonyManager telephonyManager =
+ mTelephonyManager.createForSubscriptionId(subId);
+ if (telephonyManager.getServiceState() != null) {
+ shouldShowNotification(telephonyManager.getServiceState().getState(),
+ subId);
+ }
+ break;
+ }
+ }
+ };
+
/**
* Private constructor (this is a singleton).
* @see #init(PhoneGlobals)
@@ -592,19 +628,21 @@
intent.putExtra(GsmUmtsOptions.EXTRA_SUB_ID, subId);
builder.setContentIntent(PendingIntent.getActivity(mContext, 0, intent, 0));
mNotificationManager.notifyAsUser(
- null /* tag */,
+ Integer.toString(subId) /* tag */,
SELECTED_OPERATOR_FAIL_NOTIFICATION,
builder.build(),
UserHandle.ALL);
+ mSelectedUnavailableNotify.put(subId, true);
}
/**
* Turn off the network selection "no service" notification
*/
- private void cancelNetworkSelection() {
+ private void cancelNetworkSelection(int subId) {
if (DBG) log("cancelNetworkSelection()...");
mNotificationManager.cancelAsUser(
- null /* tag */, SELECTED_OPERATOR_FAIL_NOTIFICATION, UserHandle.ALL);
+ Integer.toString(subId) /* tag */, SELECTED_OPERATOR_FAIL_NOTIFICATION,
+ UserHandle.ALL);
}
/**
@@ -645,18 +683,34 @@
+ (isManualSelection ? selectedNetworkOperatorName : ""));
}
- if (serviceState == ServiceState.STATE_OUT_OF_SERVICE && isManualSelection) {
- showNetworkSelection(selectedNetworkOperatorName, subId);
- mSelectedUnavailableNotify = true;
+ if (isManualSelection) {
+ mSelectedNetworkOperatorName.put(subId, selectedNetworkOperatorName);
+ shouldShowNotification(serviceState, subId);
} else {
- if (mSelectedUnavailableNotify) {
- cancelNetworkSelection();
- mSelectedUnavailableNotify = false;
- }
+ dismissNetworkSelectionNotification(subId);
+ clearUpNetworkSelectionNotificationParam(subId);
}
} else {
if (DBG) log("updateNetworkSelection()..." + "state = " +
serviceState + " not updating network due to invalid subId " + subId);
+ dismissNetworkSelectionNotificationForInactiveSubId();
+ }
+ }
+ }
+
+ private void dismissNetworkSelectionNotification(int subId) {
+ if (mSelectedUnavailableNotify.get(subId, false)) {
+ cancelNetworkSelection(subId);
+ mSelectedUnavailableNotify.remove(subId);
+ }
+ }
+
+ private void dismissNetworkSelectionNotificationForInactiveSubId() {
+ for (int i = 0; i < mSelectedUnavailableNotify.size(); i++) {
+ int subId = mSelectedUnavailableNotify.keyAt(i);
+ if (!mSubscriptionManager.isActiveSubId(subId)) {
+ dismissNetworkSelectionNotification(subId);
+ clearUpNetworkSelectionNotificationParam(subId);
}
}
}
@@ -677,4 +731,65 @@
private void logi(String msg) {
Log.i(LOG_TAG, msg);
}
+
+ /**
+ * In case network selection notification shows up repeatedly under
+ * unstable network condition. The logic is to check whether or not
+ * the service state keeps in no service condition for at least
+ * {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS}.
+ * And checking {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES} times.
+ * To avoid the notification showing up for the momentary state.
+ */
+ private void shouldShowNotification(int serviceState, int subId) {
+ if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
+ if (mPreviousServiceState.get(subId, STATE_UNKNOWN_SERVICE)
+ != ServiceState.STATE_OUT_OF_SERVICE) {
+ mOOSTimestamp.put(subId, getTimeStamp());
+ }
+ if ((getTimeStamp() - mOOSTimestamp.get(subId, 0L)
+ >= NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS)
+ || mPendingEventCounter.get(subId, 0)
+ > NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES) {
+ showNetworkSelection(mSelectedNetworkOperatorName.get(subId), subId);
+ clearUpNetworkSelectionNotificationParam(subId);
+ } else {
+ startPendingNetworkSelectionNotification(subId);
+ }
+ } else {
+ dismissNetworkSelectionNotification(subId);
+ }
+ mPreviousServiceState.put(subId, serviceState);
+ if (DBG) {
+ log("shouldShowNotification()..." + " subId = " + subId
+ + " serviceState = " + serviceState
+ + " mOOSTimestamp = " + mOOSTimestamp
+ + " mPendingEventCounter = " + mPendingEventCounter);
+ }
+ }
+
+ private void startPendingNetworkSelectionNotification(int subId) {
+ if (!mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
+ if (DBG) {
+ log("startPendingNetworkSelectionNotification: subId = " + subId);
+ }
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId),
+ NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS);
+ mPendingEventCounter.put(subId, mPendingEventCounter.get(subId, 0) + 1);
+ }
+ }
+
+ private void clearUpNetworkSelectionNotificationParam(int subId) {
+ if (mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
+ mHandler.removeMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId);
+ }
+ mPreviousServiceState.remove(subId);
+ mOOSTimestamp.remove(subId);
+ mPendingEventCounter.remove(subId);
+ mSelectedNetworkOperatorName.remove(subId);
+ }
+
+ private static long getTimeStamp() {
+ return SystemClock.elapsedRealtime();
+ }
}
diff --git a/src/com/android/phone/PhoneApp.java b/src/com/android/phone/PhoneApp.java
index df151bf..333e0ec 100644
--- a/src/com/android/phone/PhoneApp.java
+++ b/src/com/android/phone/PhoneApp.java
@@ -19,6 +19,7 @@
import android.app.Application;
import android.os.UserHandle;
+import com.android.phone.ecc.IsoToEccProtobufRepository;
import com.android.services.telephony.TelecomAccountRegistry;
/**
@@ -40,5 +41,10 @@
TelecomAccountRegistry.getInstance(this).setupOnBoot();
}
+
+ new Thread(() -> {
+ // Preload ECC table in background.
+ IsoToEccProtobufRepository.getInstance().loadMappingTable(PhoneApp.this);
+ }).start();
}
}
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 8f66dc2..7c7d230 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -78,6 +78,7 @@
import android.telephony.UssdResponse;
import android.telephony.VisualVoicemailSmsFilterSettings;
import android.telephony.cdma.CdmaCellLocation;
+import android.telephony.emergency.EmergencyNumber;
import android.telephony.gsm.GsmCellLocation;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsConfig;
@@ -117,6 +118,8 @@
import com.android.internal.telephony.RIL;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.SmsApplication;
+import com.android.internal.telephony.SmsApplication.SmsApplicationData;
import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.telephony.euicc.EuiccConnector;
@@ -140,6 +143,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -2055,6 +2059,40 @@
}
}
+ @Override
+ public int getSubscriptionMNOCarrierId(int subId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final Phone phone = getPhone(subId);
+ return phone == null ? TelephonyManager.UNKNOWN_CARRIER_ID : phone.getMNOCarrierId();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public int getSubscriptionPreciseCarrierId(int subId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final Phone phone = getPhone(subId);
+ return phone == null ? TelephonyManager.UNKNOWN_CARRIER_ID
+ : phone.getPreciseCarrierId();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public String getSubscriptionPreciseCarrierName(int subId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final Phone phone = getPhone(subId);
+ return phone == null ? null : phone.getPreciseCarrierName();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
//
// Internal helper methods.
//
@@ -5764,6 +5802,88 @@
}
}
+ private void ensureUserRunning(int userId) {
+ if (!mUserManager.isUserRunning(userId)) {
+ throw new IllegalStateException("User " + userId + " does not exist or not running");
+ }
+ }
+
+ /**
+ * Returns a list of SMS apps on a given user.
+ *
+ * Only the shell user (UID 2000 or 0) can call it.
+ * Target user must be running.
+ */
+ @Override
+ public String[] getSmsApps(int userId) {
+ TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getSmsApps");
+ ensureUserRunning(userId);
+
+ final Collection<SmsApplicationData> apps =
+ SmsApplication.getApplicationCollectionAsUser(mApp, userId);
+
+ String[] ret = new String[apps.size()];
+ int i = 0;
+ for (SmsApplicationData app : apps) {
+ ret[i++] = app.mPackageName;
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the default SMS app package name on a given user.
+ *
+ * Only the shell user (UID 2000 or 0) can call it.
+ * Target user must be running.
+ */
+ @Override
+ public String getDefaultSmsApp(int userId) {
+ TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getDefaultSmsApp");
+ ensureUserRunning(userId);
+
+ final ComponentName cn = SmsApplication.getDefaultSmsApplicationAsUser(mApp,
+ /* updateIfNeeded= */ true, userId);
+ return cn == null ? null : cn.getPackageName();
+ }
+
+ /**
+ * Set a package as the default SMS app on a given user.
+ *
+ * Only the shell user (UID 2000 or 0) can call it.
+ * Target user must be running.
+ */
+ @Override
+ public void setDefaultSmsApp(int userId, String packageName) {
+ TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setDefaultSmsApp");
+ ensureUserRunning(userId);
+
+ boolean found = false;
+ for (String pkg : getSmsApps(userId)) {
+ if (TextUtils.equals(packageName, pkg)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw new IllegalArgumentException("Package " + packageName + " is not an SMS app");
+ }
+
+ SmsApplication.setDefaultApplicationAsUser(packageName, mApp, userId);
+ }
+
+ @Override
+ public Map<Integer, List<EmergencyNumber>> getCurrentEmergencyNumberList(
+ String callingPackage) {
+ // TODO connect with internal content
+ return null;
+ }
+
+ @Override
+ public boolean isCurrentEmergencyNumber(String number) {
+ // TODO connect with internal content
+ return false;
+ }
+
@Override
public List<String> getCertsFromCarrierPrivilegeAccessRules(int subId) {
enforceReadPrivilegedPermission("getCertsFromCarrierPrivilegeAccessRules");
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index 4acb46b..1a25ae3 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -18,6 +18,7 @@
import android.os.RemoteException;
import android.os.ShellCommand;
+import android.os.UserHandle;
import android.telephony.SubscriptionManager;
import android.util.Log;
@@ -39,11 +40,16 @@
private static final int DEFAULT_PHONE_ID = 0;
private static final String IMS_SUBCOMMAND = "ims";
+ private static final String SMS_SUBCOMMAND = "sms";
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";
private static final String IMS_DISABLE = "disable";
+ private static final String SMS_GET_APPS = "get-apps";
+ private static final String SMS_GET_DEFAULT_APP = "get-default-app";
+ private static final String SMS_SET_DEFAULT_APP = "set-default-app";
+
// Take advantage of existing methods that already contain permissions checks when possible.
private final ITelephony mInterface;
@@ -61,6 +67,9 @@
case IMS_SUBCOMMAND: {
return handleImsCommand();
}
+ case SMS_SUBCOMMAND: {
+ return handleSmsCommand();
+ }
default: {
return handleDefaultCommands(cmd);
}
@@ -75,7 +84,10 @@
pw.println(" Print this help text.");
pw.println(" ims");
pw.println(" IMS Commands.");
+ pw.println(" sms");
+ pw.println(" SMS Commands.");
onHelpIms();
+ onHelpSms();
}
private void onHelpIms() {
@@ -103,6 +115,17 @@
pw.println(" slot if none is specified.");
}
+ private void onHelpSms() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("SMS Commands:");
+ pw.println(" sms get-apps [--user USER_ID]");
+ pw.println(" Print all SMS apps on a user.");
+ pw.println(" sms get-default-app [--user USER_ID]");
+ pw.println(" Get the default SMS app.");
+ pw.println(" sms set-default-app [--user USER_ID] PACKAGE_NAME");
+ pw.println(" Set PACKAGE_NAME as the default SMS app.");
+ }
+
private int handleImsCommand() {
String arg = getNextArg();
if (arg == null) {
@@ -296,4 +319,85 @@
}
return slotId;
}
+
+ private int handleSmsCommand() {
+ String arg = getNextArg();
+ if (arg == null) {
+ onHelpSms();
+ return 0;
+ }
+
+ try {
+ switch (arg) {
+ case SMS_GET_APPS: {
+ return handleSmsGetApps();
+ }
+ case SMS_GET_DEFAULT_APP: {
+ return handleSmsGetDefaultApp();
+ }
+ case SMS_SET_DEFAULT_APP: {
+ return handleSmsSetDefaultApp();
+ }
+ default:
+ getErrPrintWriter().println("Unknown command " + arg);
+ }
+ } catch (RemoteException e) {
+ getErrPrintWriter().println("RemoteException: " + e.getMessage());
+ }
+
+ return -1;
+ }
+
+ private int maybeParseUserIdArg() {
+ int userId = UserHandle.USER_SYSTEM;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "--user": {
+ try {
+ userId = Integer.parseInt(getNextArgRequired());
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Invalid user ID for --user");
+ return -1;
+ }
+ break;
+ }
+ }
+ }
+ return userId;
+ }
+
+ private int handleSmsGetApps() throws RemoteException {
+ final int userId = maybeParseUserIdArg();
+ if (userId < 0) {
+ return -1;
+ }
+
+ for (String packageName : mInterface.getSmsApps(userId)) {
+ getOutPrintWriter().println(packageName);
+ }
+ return 0;
+ }
+
+ private int handleSmsGetDefaultApp() throws RemoteException {
+ final int userId = maybeParseUserIdArg();
+ if (userId < 0) {
+ return -1;
+ }
+
+ getOutPrintWriter().println(mInterface.getDefaultSmsApp(userId));
+ return 0;
+ }
+
+ private int handleSmsSetDefaultApp() throws RemoteException {
+ final int userId = maybeParseUserIdArg();
+ if (userId < 0) {
+ return -1;
+ }
+
+ String packageName = getNextArgRequired();
+ mInterface.setDefaultSmsApp(userId, packageName);
+ getOutPrintWriter().println("SMS app set to " + mInterface.getDefaultSmsApp(userId));
+ return 0;
+ }
}
diff --git a/src/com/android/phone/ecc/EccInfoHelper.java b/src/com/android/phone/ecc/EccInfoHelper.java
index cd47dde..c471c4b 100644
--- a/src/com/android/phone/ecc/EccInfoHelper.java
+++ b/src/com/android/phone/ecc/EccInfoHelper.java
@@ -50,6 +50,32 @@
private static final boolean DBG = false;
private static final String LOG_TAG = "EccInfoHelper";
+ /**
+ * Check if current CountryEccInfo is available for current environment.
+ */
+ public static boolean isCountryEccInfoAvailable(Context context, String countryIso) {
+ CountryEccInfo countryEccInfo;
+ try {
+ countryEccInfo = IsoToEccProtobufRepository.getInstance()
+ .getCountryEccInfo(context, countryIso);
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Failed to retrieve ECC: ", e);
+ return false;
+ }
+
+ if (countryEccInfo == null) {
+ return false;
+ }
+ for (EccInfo entry : countryEccInfo.getEccInfoList()) {
+ if (!PhoneNumberUtils.isEmergencyNumber(entry.getNumber())) {
+ // The CountryEccInfo is unavailable if any ecc number in the local table was
+ // declined.
+ return false;
+ }
+ }
+ return true;
+ }
+
// country ISO to ECC list data source
private IsoToEccRepository mEccRepo;
@@ -136,7 +162,8 @@
}.execute();
}
- private @NonNull CountryEccInfo getDialableCountryEccInfo(CountryEccInfo countryEccInfo) {
+ @NonNull
+ private CountryEccInfo getDialableCountryEccInfo(CountryEccInfo countryEccInfo) {
ArrayList<EccInfo> dialableECCList = new ArrayList<>();
String dialableFallback = null;
@@ -155,7 +182,8 @@
return new CountryEccInfo(dialableFallback, dialableECCList);
}
- private @Nullable String getCurrentCountryIso(@NonNull Context context) {
+ @Nullable
+ private String getCurrentCountryIso(@NonNull Context context) {
// Do not detect country ISO if airplane mode is on
int airplaneMode = Settings.System.getInt(context.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 0);
@@ -185,7 +213,8 @@
// XXX: According to ServiceStateTracker implementation, to actually get current cell info,
// this method must be called in a separate thread from ServiceStateTracker, which is the
// main thread of Telephony service.
- private @Nullable String getCurrentMccFromCellInfo(@NonNull Context context) {
+ @Nullable
+ private String getCurrentMccFromCellInfo(@NonNull Context context) {
// retrieve mcc info from base station even no SIM present.
TelephonyManager tm = (TelephonyManager) context.getSystemService(
Context.TELEPHONY_SERVICE);
diff --git a/src/com/android/phone/ecc/IsoToEccProtobufRepository.java b/src/com/android/phone/ecc/IsoToEccProtobufRepository.java
index 817ff1d..0cd3108 100644
--- a/src/com/android/phone/ecc/IsoToEccProtobufRepository.java
+++ b/src/com/android/phone/ecc/IsoToEccProtobufRepository.java
@@ -40,7 +40,22 @@
public class IsoToEccProtobufRepository implements IsoToEccRepository {
private static final String LOG_TAG = "EccRepository";
- private Map<String, CountryEccInfo> mEccTable = null;
+ private static IsoToEccProtobufRepository sInstance;
+
+ /**
+ * Returns the singleton instance of IsoToEccProtobufRepository
+ */
+ public static synchronized IsoToEccProtobufRepository getInstance() {
+ if (sInstance == null) {
+ sInstance = new IsoToEccProtobufRepository();
+ }
+ return sInstance;
+ }
+
+ private final Map<String, CountryEccInfo> mEccTable = new HashMap<>();
+
+ private IsoToEccProtobufRepository() {
+ }
@Override
@Nullable
@@ -50,24 +65,28 @@
return null;
}
- if (mEccTable == null) {
- mEccTable = initMappingTable(context);
+ synchronized (mEccTable) {
+ return mEccTable.get(iso.toUpperCase());
}
- return mEccTable.get(iso.toUpperCase());
}
- private Map<String, CountryEccInfo> initMappingTable(@NonNull Context context)
- throws IOException {
+ /**
+ * Loads the mapping table.
+ */
+ public void loadMappingTable(@NonNull Context context) {
ProtobufEccData.AllInfo allEccData = null;
long startTime = SystemClock.uptimeMillis();
- allEccData = parseEccData(new BufferedInputStream(
- context.getAssets().open("eccdata")));
+ try {
+ allEccData = parseEccData(new BufferedInputStream(
+ context.getAssets().open("eccdata")));
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Failed to retrieve ECC: ", e);
+ }
long endTime = SystemClock.uptimeMillis();
if (allEccData == null) {
- // Returns an empty table.
- return new HashMap<>();
+ return;
}
if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
@@ -77,16 +96,17 @@
}
// Converts to run-time data from Protobuf data.
- Map<String, CountryEccInfo> table = new HashMap<>();
- for (ProtobufEccData.CountryInfo countryData : allEccData.getCountriesList()) {
- if (countryData.hasIsoCode()) {
- CountryEccInfo countryInfo = loadCountryEccInfo(countryData);
- if (countryInfo != null) {
- table.put(countryData.getIsoCode().toUpperCase(), countryInfo);
+ synchronized (mEccTable) {
+ mEccTable.clear();
+ for (ProtobufEccData.CountryInfo countryData : allEccData.getCountriesList()) {
+ if (countryData.hasIsoCode()) {
+ CountryEccInfo countryInfo = loadCountryEccInfo(countryData);
+ if (countryInfo != null) {
+ mEccTable.put(countryData.getIsoCode().toUpperCase(), countryInfo);
+ }
}
}
}
- return table;
}
private ProtobufEccData.AllInfo parseEccData(InputStream input) throws IOException {
diff --git a/src/com/android/phone/otasp/OtaspActivationService.java b/src/com/android/phone/otasp/OtaspActivationService.java
index c41640c..7490880 100644
--- a/src/com/android/phone/otasp/OtaspActivationService.java
+++ b/src/com/android/phone/otasp/OtaspActivationService.java
@@ -28,6 +28,7 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import com.android.internal.telephony.GsmCdmaConnection;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.phone.PhoneGlobals;
@@ -45,8 +46,10 @@
public class OtaspActivationService extends Service {
private static final String TAG = OtaspActivationService.class.getSimpleName();
private static final boolean DBG = true;
- /* non-interactive otasp number */
- private static final String OTASP_NUMBER = "*22899";
+ /**
+ * non-interactive otasp number
+ */
+ private static final String OTASP_NUMBER = GsmCdmaConnection.OTASP_NUMBER;
/**
* Otasp call follows with SIM reloading which might triggers a retry loop on activation
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index 611d147..9ed34e7 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -173,6 +173,7 @@
case android.telephony.DisconnectCause.CANT_CALL_WHILE_RINGING:
case android.telephony.DisconnectCause.CALLING_DISABLED:
case android.telephony.DisconnectCause.TOO_MANY_ONGOING_CALLS:
+ case android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS:
case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER:
case android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING:
case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_USSD:
@@ -337,11 +338,12 @@
case android.telephony.DisconnectCause.TOO_MANY_ONGOING_CALLS:
resourceId = R.string.callFailed_too_many_calls;
break;
-
case android.telephony.DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL:
resourceId = R.string.incall_error_power_off;
break;
-
+ case android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS:
+ resourceId = R.string.callFailed_otasp_provisioning_in_process;
+ break;
default:
break;
}
@@ -717,11 +719,12 @@
case android.telephony.DisconnectCause.TOO_MANY_ONGOING_CALLS:
resourceId = R.string.callFailed_too_many_calls;
break;
-
case android.telephony.DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL:
resourceId = R.string.incall_error_power_off;
break;
-
+ case android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS:
+ resourceId = R.string.callFailed_otasp_provisioning_in_process;
+ break;
default:
break;
}
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 07754c6..f986994 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -1140,6 +1140,9 @@
case CallStateException.ERROR_TOO_MANY_CALLS:
cause = android.telephony.DisconnectCause.TOO_MANY_ONGOING_CALLS;
break;
+ case CallStateException.ERROR_OTASP_PROVISIONING_IN_PROCESS:
+ cause = android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS;
+ break;
}
connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
cause, e.getMessage(), phone.getPhoneId()));
diff --git a/testapps/TelephonyRegistryTestApp/AndroidManifest.xml b/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
index 5f19509..708ea66 100644
--- a/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
+++ b/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
@@ -18,6 +18,7 @@
package="com.android.phone.testapps.telephonyregistry">
<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"/>
<application android:label="TelephonyRegistryTestApp">
<activity
android:name=".TelephonyRegistryTestApp"
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 8593245..74cafcd 100644
--- a/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
+++ b/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
@@ -54,7 +54,7 @@
put(PhoneStateListener.LISTEN_PRECISE_CALL_STATE, "PRECISE_CALL_STATE");
put(PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE,
"PRECISE_DATA_CONNECTION_STATE");
- put(PhoneStateListener.LISTEN_VOLTE_STATE, "VOLTE_STATE");
+ put(PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED, "SRVCC_STATE");
put(PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE, "CARRIER_NETWORK_CHANGE");
put(PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE, "VOICE_ACTIVATION_STATE");
put(PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE, "DATA_ACTIVATION_STATE");
@@ -71,6 +71,11 @@
notify("onCellInfoChanged", cellInfo);
}
+ @Override
+ public void onSrvccStateChanged(int srvccState) {
+ notify("onSrvccStateChanged", srvccState);
+ }
+
private void notify(String method, Object data) {
Notification.Builder builder = new Notification.Builder(TelephonyRegistryTestApp.this,
NOTIFICATION_CHANNEL);