Merge "Fix issue where verstat is not propagated to Telecom."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 16ad93a..a7c2073 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -322,7 +322,7 @@
<activity android:name="GsmUmtsCallBarringOptions"
android:label="@string/labelCallBarring"
android:configChanges="orientation|screenSize|keyboardHidden"
- android:theme="@style/CallSettingsWithoutDividerTheme">
+ android:theme="@style/DialerSettingsLight">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
diff --git a/res/values/config.xml b/res/values/config.xml
index d4e4c79..2f0fcc6 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -257,6 +257,16 @@
audio stream which the remote party will be able to hear. -->
<bool name="config_support_telephony_audio_device">false</bool>
+ <!-- Whether the device supports dialing emergency RTT calls when there's no SIM card installed
+ -->
+ <bool name="config_support_simless_emergency_rtt">false</bool>
+
+ <!-- Array of countries that support sim-less emergency RTT calls. Values should be
+ ISO3166 country codes in lowercase. -->
+ <string-array name="config_simless_emergency_rtt_supported_countries" translatable="false">
+ <item>us</item>
+ </string-array>
+
<string-array translatable="false" name="config_volte_provision_error_on_publish_response">
<item>403 not authorized for presence</item>
</string-array>
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 9faffa1..ef83ead 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -97,6 +97,7 @@
"phone_account_settings_preference_screen";
private static final String ENABLE_VIDEO_CALLING_KEY = "button_enable_video_calling";
+ private static final String BUTTON_VP_KEY = "button_voice_privacy_key";
private Phone mPhone;
private ImsManager mImsMgr;
@@ -397,6 +398,9 @@
if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
prefSet.removePreference(fdnButton);
addPreferencesFromResource(R.xml.cdma_call_privacy);
+ CdmaVoicePrivacySwitchPreference buttonVoicePrivacy =
+ (CdmaVoicePrivacySwitchPreference) findPreference(BUTTON_VP_KEY);
+ buttonVoicePrivacy.setPhone(mPhone);
} else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
if (mPhone.getIccCard() == null || !mPhone.getIccCard().getIccFdnAvailable()) {
prefSet.removePreference(fdnButton);
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index a816c89..b969ce4 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (c) 2015, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -37,6 +38,7 @@
import android.os.IBinder;
import android.os.Message;
import android.os.PersistableBundle;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -47,6 +49,8 @@
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Log;
@@ -71,6 +75,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
/**
* CarrierConfigLoader binds to privileged carrier apps to fetch carrier config overlays.
@@ -153,6 +158,12 @@
// SharedPreferences key for last known build fingerprint.
private static final String KEY_FINGERPRINT = "build_fingerprint";
+ // Argument for #dump that indicates we should also call the default and specified carrier
+ // service's #dump method. In multi-SIM devices, it's possible that carrier A is on SIM 1 and
+ // carrier B is on SIM 2, in which case we should not dump carrier B's service when carrier A
+ // requested the dump.
+ private static final String DUMP_ARG_REQUESTING_PACKAGE = "--requesting-package";
+
// Handler to process various events.
//
// For each phoneId, the event sequence should be:
@@ -174,42 +185,39 @@
@Override
public void handleMessage(Message msg) {
final int phoneId = msg.arg1;
- logWithLocalLog("mHandler: " + msg.what + " phoneId: " + phoneId);
+ logdWithLocalLog("mHandler: " + msg.what + " phoneId: " + phoneId);
if (!SubscriptionManager.isValidPhoneId(phoneId)
&& msg.what != EVENT_MULTI_SIM_CONFIG_CHANGED) {
return;
}
switch (msg.what) {
- case EVENT_CLEAR_CONFIG:
- {
+ case EVENT_CLEAR_CONFIG: {
clearConfigForPhone(phoneId, true);
break;
}
- case EVENT_SYSTEM_UNLOCKED:
- {
+ case EVENT_SYSTEM_UNLOCKED: {
for (int i = 0; i < TelephonyManager.from(mContext).getActiveModemCount();
++i) {
// When user unlock device, we should only try to send broadcast again if we
// have sent it before unlock. This will avoid we try to load carrier config
// when SIM is still loading when unlock happens.
if (mHasSentConfigChange[i]) {
- logWithLocalLog("System unlocked");
+ logdWithLocalLog("System unlocked");
updateConfigForPhoneId(i);
}
}
break;
}
- case EVENT_PACKAGE_CHANGED:
- {
+ case EVENT_PACKAGE_CHANGED: {
final String carrierPackageName = (String) msg.obj;
// Only update if there are cached config removed to avoid updating config for
// unrelated packages.
if (clearCachedConfigForPackage(carrierPackageName)) {
int numPhones = TelephonyManager.from(mContext).getActiveModemCount();
for (int i = 0; i < numPhones; ++i) {
- logWithLocalLog("Package changed: " + carrierPackageName
+ logdWithLocalLog("Package changed: " + carrierPackageName
+ ", phone=" + i);
updateConfigForPhoneId(i);
}
@@ -217,13 +225,12 @@
break;
}
- case EVENT_DO_FETCH_DEFAULT:
- {
+ case EVENT_DO_FETCH_DEFAULT: {
// Restore persistent override values.
PersistableBundle config = restoreConfigFromXml(
mPlatformCarrierConfigPackage, OVERRIDE_PACKAGE_ADDITION, phoneId);
if (config != null) {
- log("Loaded persistent override config from XML. package="
+ logd("Loaded persistent override config from XML. package="
+ mPlatformCarrierConfigPackage
+ " phoneId=" + phoneId);
mPersistentOverrideConfigs[phoneId] = config;
@@ -231,7 +238,7 @@
config = restoreConfigFromXml(mPlatformCarrierConfigPackage, "", phoneId);
if (config != null) {
- log(
+ logd(
"Loaded config from XML. package="
+ mPlatformCarrierConfigPackage
+ " phoneId="
@@ -261,8 +268,7 @@
break;
}
- case EVENT_CONNECTED_TO_DEFAULT:
- {
+ case EVENT_CONNECTED_TO_DEFAULT: {
removeMessages(EVENT_BIND_DEFAULT_TIMEOUT);
final CarrierServiceConnection conn = (CarrierServiceConnection) msg.obj;
// If new service connection has been created, unbind.
@@ -292,7 +298,7 @@
PersistableBundle config =
resultData.getParcelable(KEY_CONFIG_BUNDLE);
saveConfigToXml(mPlatformCarrierConfigPackage, "", phoneId,
- carrierId, config);
+ carrierId, config);
mConfigFromDefaultApp[phoneId] = config;
sendMessage(
obtainMessage(
@@ -304,7 +310,7 @@
ICarrierService carrierService =
ICarrierService.Stub.asInterface(conn.service);
carrierService.getCarrierConfig(carrierId, resultReceiver);
- logWithLocalLog("fetch config for default app: "
+ logdWithLocalLog("Fetch config for default app: "
+ mPlatformCarrierConfigPackage
+ " carrierid: " + carrierId.toString());
} catch (RemoteException e) {
@@ -320,9 +326,8 @@
}
case EVENT_BIND_DEFAULT_TIMEOUT:
- case EVENT_FETCH_DEFAULT_TIMEOUT:
- {
- loge("bind/fetch time out from " + mPlatformCarrierConfigPackage);
+ case EVENT_FETCH_DEFAULT_TIMEOUT: {
+ loge("Bind/fetch time out from " + mPlatformCarrierConfigPackage);
removeMessages(EVENT_FETCH_DEFAULT_TIMEOUT);
// If we attempted to bind to the app, but the service connection is null due to
// the race condition that clear config event happens before bind/fetch complete
@@ -337,8 +342,7 @@
break;
}
- case EVENT_FETCH_DEFAULT_DONE:
- {
+ case EVENT_FETCH_DEFAULT_DONE: {
// If we attempted to bind to the app, but the service connection is null, then
// config was cleared while we were waiting and we should not continue.
if (!msg.getData().getBoolean("loaded_from_xml", false)
@@ -347,7 +351,7 @@
}
final String carrierPackageName = getCarrierPackageForPhoneId(phoneId);
if (carrierPackageName != null) {
- log("Found carrier config app: " + carrierPackageName);
+ logd("Found carrier config app: " + carrierPackageName);
sendMessage(obtainMessage(EVENT_DO_FETCH_CARRIER, phoneId, -1));
} else {
notifySubscriptionInfoUpdater(phoneId);
@@ -355,13 +359,12 @@
break;
}
- case EVENT_DO_FETCH_CARRIER:
- {
+ case EVENT_DO_FETCH_CARRIER: {
final String carrierPackageName = getCarrierPackageForPhoneId(phoneId);
final PersistableBundle config =
restoreConfigFromXml(carrierPackageName, "", phoneId);
if (config != null) {
- log(
+ logd(
"Loaded config from XML. package="
+ carrierPackageName
+ " phoneId="
@@ -372,26 +375,22 @@
sendMessage(newMsg);
} else {
// No cached config, so fetch it from a carrier app.
- if (carrierPackageName != null
- && bindToConfigPackage(
- carrierPackageName,
- phoneId,
- EVENT_CONNECTED_TO_CARRIER)) {
+ if (carrierPackageName != null && bindToConfigPackage(carrierPackageName,
+ phoneId, EVENT_CONNECTED_TO_CARRIER)) {
sendMessageDelayed(
obtainMessage(EVENT_BIND_CARRIER_TIMEOUT, phoneId, -1),
BIND_TIMEOUT_MILLIS);
} else {
// Send broadcast if bind fails.
broadcastConfigChangedIntent(phoneId);
- loge("bind to carrier app: " + carrierPackageName + " fails");
+ loge("Bind to carrier app: " + carrierPackageName + " fails");
notifySubscriptionInfoUpdater(phoneId);
}
}
break;
}
- case EVENT_CONNECTED_TO_CARRIER:
- {
+ case EVENT_CONNECTED_TO_CARRIER: {
removeMessages(EVENT_BIND_CARRIER_TIMEOUT);
final CarrierServiceConnection conn = (CarrierServiceConnection) msg.obj;
// If new service connection has been created, unbind.
@@ -435,7 +434,7 @@
ICarrierService carrierService =
ICarrierService.Stub.asInterface(conn.service);
carrierService.getCarrierConfig(carrierId, resultReceiver);
- logWithLocalLog("fetch config for carrier app: "
+ logdWithLocalLog("Fetch config for carrier app: "
+ getCarrierPackageForPhoneId(phoneId)
+ " carrierid: " + carrierId.toString());
} catch (RemoteException e) {
@@ -450,9 +449,8 @@
}
case EVENT_BIND_CARRIER_TIMEOUT:
- case EVENT_FETCH_CARRIER_TIMEOUT:
- {
- loge("bind/fetch from carrier app timeout");
+ case EVENT_FETCH_CARRIER_TIMEOUT: {
+ loge("Bind/fetch from carrier app timeout");
removeMessages(EVENT_FETCH_CARRIER_TIMEOUT);
// If we attempted to bind to the app, but the service connection is null due to
// the race condition that clear config event happens before bind/fetch complete
@@ -466,8 +464,7 @@
notifySubscriptionInfoUpdater(phoneId);
break;
}
- case EVENT_FETCH_CARRIER_DONE:
- {
+ case EVENT_FETCH_CARRIER_DONE: {
// If we attempted to bind to the app, but the service connection is null, then
// config was cleared while we were waiting and we should not continue.
if (!msg.getData().getBoolean("loaded_from_xml", false)
@@ -478,13 +475,12 @@
break;
}
- case EVENT_CHECK_SYSTEM_UPDATE:
- {
+ case EVENT_CHECK_SYSTEM_UPDATE: {
SharedPreferences sharedPrefs =
PreferenceManager.getDefaultSharedPreferences(mContext);
final String lastFingerprint = sharedPrefs.getString(KEY_FINGERPRINT, null);
if (!Build.FINGERPRINT.equals(lastFingerprint)) {
- log(
+ logd(
"Build fingerprint changed. old: "
+ lastFingerprint
+ " new: "
@@ -543,7 +539,7 @@
mHasSentConfigChange = new boolean[numPhones];
// Make this service available through ServiceManager.
ServiceManager.addService(Context.CARRIER_CONFIG_SERVICE, this);
- log("CarrierConfigLoader has started");
+ logd("CarrierConfigLoader has started");
mSubscriptionInfoUpdater = PhoneFactory.getSubscriptionInfoUpdater();
mHandler.sendEmptyMessage(EVENT_CHECK_SYSTEM_UPDATE);
}
@@ -638,17 +634,17 @@
}
}
intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, phoneId);
- log("Broadcast CARRIER_CONFIG_CHANGED for phone " + phoneId);
+ logd("Broadcast CARRIER_CONFIG_CHANGED for phone " + phoneId);
mContext.sendBroadcast(intent);
mHasSentConfigChange[phoneId] = true;
}
/** Binds to the default or carrier config app. */
private boolean bindToConfigPackage(String pkgName, int phoneId, int eventId) {
- logWithLocalLog("Binding to " + pkgName + " for phone " + phoneId);
+ logdWithLocalLog("Binding to " + pkgName + " for phone " + phoneId);
Intent carrierService = new Intent(CarrierService.CARRIER_SERVICE_INTERFACE);
carrierService.setPackage(pkgName);
- mServiceConnection[phoneId] = new CarrierServiceConnection(phoneId, eventId);
+ mServiceConnection[phoneId] = new CarrierServiceConnection(phoneId, pkgName, eventId);
try {
if (mContext.bindService(carrierService, mServiceConnection[phoneId],
Context.BIND_AUTO_CREATE)) {
@@ -756,9 +752,9 @@
*
* @param packageName the name of the package from which we fetched this bundle.
* @param extraString An extra string to be used in the XML file name.
- * @param phoneId the phone ID.
- * @param carrierId contains all carrier-identifying information.
- * @param config the bundle to be written. Null will be treated as an empty bundle.
+ * @param phoneId the phone ID.
+ * @param carrierId contains all carrier-identifying information.
+ * @param config the bundle to be written. Null will be treated as an empty bundle.
*/
private void saveConfigToXml(String packageName, @NonNull String extraString, int phoneId,
CarrierIdentifier carrierId, PersistableBundle config) {
@@ -789,7 +785,8 @@
return;
}
- logWithLocalLog("save config to xml, packagename: " + packageName + " phoneId: " + phoneId);
+ logdWithLocalLog(
+ "Save config to xml, packagename: " + packageName + " phoneId: " + phoneId);
FileOutputStream outFile = null;
try {
@@ -800,8 +797,7 @@
config.writeToStream(outFile);
outFile.flush();
outFile.close();
- }
- catch (IOException e) {
+ } catch (IOException e) {
loge(e.toString());
}
}
@@ -817,9 +813,9 @@
*
* @param packageName the name of the package from which we fetched this bundle.
* @param extraString An extra string to be used in the XML file name.
- * @param phoneId the phone ID.
+ * @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.
+ * version does not match, or reading config fails.
*/
private PersistableBundle restoreConfigFromXml(String packageName, @NonNull String extraString,
int phoneId) {
@@ -859,13 +855,11 @@
}
inFile.close();
- }
- catch (FileNotFoundException e) {
+ } catch (FileNotFoundException e) {
// Missing file is normal occurrence that might occur with a new sim or when restoring
// an override file during boot and should not be treated as an error.
- if (file != null) log("File not found: " + file.getPath());
- }
- catch (IOException e) {
+ if (file != null) logd("File not found: " + file.getPath());
+ } catch (IOException e) {
loge(e.toString());
}
@@ -894,7 +888,7 @@
});
if (packageFiles == null || packageFiles.length < 1) return false;
for (File f : packageFiles) {
- log("deleting " + f.getName());
+ logd("Deleting " + f.getName());
f.delete();
}
return true;
@@ -902,7 +896,7 @@
/** Builds a canonical file name for a config file. */
private String getFilenameForConfig(@NonNull String packageName, @NonNull String extraString,
- @NonNull String iccid, int cid) {
+ @NonNull String iccid, int cid) {
// the same carrier should have a single copy of XML file named after carrier id.
// However, it's still possible that platform doesn't recognize the current sim carrier,
// we will use iccid + carrierid as the canonical file name. carrierid can also handle the
@@ -945,12 +939,14 @@
}
@Override
- public @NonNull PersistableBundle getConfigForSubId(int subId, String callingPackage) {
+ @NonNull
+ public PersistableBundle getConfigForSubId(int subId, String callingPackage) {
return getConfigForSubIdWithFeature(subId, callingPackage, null);
}
@Override
- public @NonNull PersistableBundle getConfigForSubIdWithFeature(int subId, String callingPackage,
+ @NonNull
+ public PersistableBundle getConfigForSubIdWithFeature(int subId, String callingPackage,
String callingFeatureId) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, callingPackage,
callingFeatureId, "getCarrierConfig")) {
@@ -994,7 +990,7 @@
//TODO: Also check for SHELL UID to restrict this method to testing only (b/131326259)
int phoneId = SubscriptionManager.getPhoneId(subscriptionId);
if (!SubscriptionManager.isValidPhoneId(phoneId)) {
- log("Ignore invalid phoneId: " + phoneId + " for subId: " + subscriptionId);
+ logd("Ignore invalid phoneId: " + phoneId + " for subId: " + subscriptionId);
return;
}
overrideConfig(mOverrideConfigs, phoneId, overrides);
@@ -1033,7 +1029,7 @@
public void notifyConfigChangedForSubId(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
if (!SubscriptionManager.isValidPhoneId(phoneId)) {
- log("Ignore invalid phoneId: " + phoneId + " for subId: " + subId);
+ logd("Ignore invalid phoneId: " + phoneId + " for subId: " + subId);
return;
}
@@ -1055,7 +1051,7 @@
public void updateConfigForPhoneId(int phoneId, String simState) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE, null);
- logWithLocalLog("update config for phoneId: " + phoneId + " simState: " + simState);
+ logdWithLocalLog("Update config for phoneId: " + phoneId + " simState: " + simState);
if (!SubscriptionManager.isValidPhoneId(phoneId)) {
return;
}
@@ -1083,44 +1079,78 @@
return mPlatformCarrierConfigPackage;
}
+ private void unbindIfBound(Context context, CarrierServiceConnection conn,
+ int phoneId) {
+ if (mServiceBound[phoneId]) {
+ mServiceBound[phoneId] = false;
+ context.unbindService(conn);
+ }
+ }
+
+ /**
+ * If {@code args} contains {@link #DUMP_ARG_REQUESTING_PACKAGE} and a following package name,
+ * we'll also call {@link IBinder#dump} on the default carrier service (if bound) and the
+ * specified carrier service (if bound). Typically, this is done for connectivity bug reports
+ * where we don't call {@code dumpsys activity service all-non-platform} because that contains
+ * too much info, but we still want to let carrier apps include their diagnostics.
+ */
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ IndentingPrintWriter indentPW = new IndentingPrintWriter(pw, " ");
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump carrierconfig from from pid="
+ indentPW.println("Permission Denial: can't dump carrierconfig from from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
return;
}
- pw.println("CarrierConfigLoader: " + this);
- for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
- pw.println("Phone Id = " + i);
- // display default values in CarrierConfigManager
- printConfig(CarrierConfigManager.getDefaultConfig(), pw,
- "Default Values from CarrierConfigManager");
- pw.println("");
- // display ConfigFromDefaultApp
- printConfig(mConfigFromDefaultApp[i], pw, "mConfigFromDefaultApp");
- pw.println("");
- // display ConfigFromCarrierApp
- printConfig(mConfigFromCarrierApp[i], pw, "mConfigFromCarrierApp");
- pw.println("");
- printConfig(mPersistentOverrideConfigs[i], pw, "mPersistentOverrideConfigs");
- pw.println("");
- printConfig(mOverrideConfigs[i], pw, "mOverrideConfigs");
+ String requestingPackage = null;
+ int requestingPackageIndex = ArrayUtils.indexOf(args, DUMP_ARG_REQUESTING_PACKAGE);
+ if (requestingPackageIndex >= 0 && requestingPackageIndex < args.length - 1
+ && !TextUtils.isEmpty(args[requestingPackageIndex + 1])) {
+ requestingPackage = args[requestingPackageIndex + 1];
+ // Throws a SecurityException if the caller is impersonating another app in an effort to
+ // dump extra info (which may contain PII the caller doesn't have a right to).
+ enforceCallerIsSystemOrRequestingPackage(requestingPackage);
}
- pw.println("CarrierConfigLoadingLog=");
- mCarrierConfigLoadingLog.dump(fd, pw, args);
+ indentPW.println("CarrierConfigLoader: " + this);
+ for (int i = 0; i < TelephonyManager.from(mContext).getActiveModemCount(); i++) {
+ indentPW.println("Phone Id = " + i);
+ // display default values in CarrierConfigManager
+ printConfig(CarrierConfigManager.getDefaultConfig(), indentPW,
+ "Default Values from CarrierConfigManager");
+ // display ConfigFromDefaultApp
+ printConfig(mConfigFromDefaultApp[i], indentPW, "mConfigFromDefaultApp");
+ // display ConfigFromCarrierApp
+ printConfig(mConfigFromCarrierApp[i], indentPW, "mConfigFromCarrierApp");
+ printConfig(mPersistentOverrideConfigs[i], indentPW, "mPersistentOverrideConfigs");
+ printConfig(mOverrideConfigs[i], indentPW, "mOverrideConfigs");
+ }
+
+ indentPW.println("CarrierConfigLoadingLog=");
+ mCarrierConfigLoadingLog.dump(fd, indentPW, args);
+
+ if (requestingPackage != null) {
+ logd("Including default and requesting package " + requestingPackage
+ + " carrier services in dump");
+ indentPW.println("");
+ indentPW.println("Connected services");
+ dumpCarrierServiceIfBound(fd, indentPW, "Default config package",
+ mPlatformCarrierConfigPackage, false /* considerCarrierPrivileges */);
+ dumpCarrierServiceIfBound(fd, indentPW, "Requesting package", requestingPackage,
+ true /* considerCarrierPrivileges */);
+ }
}
- private void printConfig(PersistableBundle configApp, PrintWriter pw, String name) {
- IndentingPrintWriter indentPW = new IndentingPrintWriter(pw, " ");
+ private void printConfig(PersistableBundle configApp, IndentingPrintWriter indentPW,
+ String name) {
+ indentPW.increaseIndent();
if (configApp == null) {
- indentPW.increaseIndent();
indentPW.println(name + " : null ");
+ indentPW.decreaseIndent();
+ indentPW.println("");
return;
}
- indentPW.increaseIndent();
indentPW.println(name + " : ");
List<String> sortedKeys = new ArrayList<String>(configApp.keySet());
Collections.sort(sortedKeys);
@@ -1136,48 +1166,162 @@
indentPW.println(key + " = " + configApp.get(key));
}
}
+ indentPW.decreaseIndent();
+ indentPW.decreaseIndent();
+ indentPW.decreaseIndent();
+ indentPW.println("");
}
- private void unbindIfBound(Context context, CarrierServiceConnection conn,
- int phoneId) {
- if (mServiceBound[phoneId]) {
- mServiceBound[phoneId] = false;
- context.unbindService(conn);
+ /**
+ * Passes without problem when one of these conditions is true:
+ * - The caller is a privileged UID (e.g. for dumpstate.cpp generating a bug report, where the
+ * system knows the true caller plumbed in through the {@link android.os.BugreportManager} API).
+ * - The caller's UID matches the supplied package.
+ *
+ * @throws SecurityException if none of the above conditions are met.
+ */
+ private void enforceCallerIsSystemOrRequestingPackage(String requestingPackage)
+ throws SecurityException {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID
+ || callingUid == Process.SHELL_UID || callingUid == Process.PHONE_UID) {
+ // Bug reports (dumpstate.cpp) run as SHELL, and let some other privileged UIDs through
+ // as well.
+ return;
}
+ // An app is trying to dump extra detail, block it if they aren't who they claim to be.
+ AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+ if (appOps == null) {
+ throw new SecurityException("No AppOps");
+ }
+ // Will throw a SecurityException if the UID and package don't match.
+ appOps.checkPackage(callingUid, requestingPackage);
+ }
+
+ /**
+ * Searches for one or more appropriate {@link CarrierService} instances to dump based on the
+ * current connections.
+ *
+ * @param targetPkgName the target package name to dump carrier services for
+ * @param considerCarrierPrivileges if true, allow a carrier service to be dumped if it shares
+ * carrier privileges with {@code targetPkgName};
+ * otherwise, only dump a carrier service if it is {@code
+ * targetPkgName}
+ */
+ private void dumpCarrierServiceIfBound(FileDescriptor fd, IndentingPrintWriter indentPW,
+ String prefix, String targetPkgName, boolean considerCarrierPrivileges) {
+ // Null package is possible if it's early in the boot process, there was a recent crash, we
+ // loaded the config from XML most recently, or a SIM slot is empty. Carrier apps with
+ // long-lived bindings should typically get dumped here regardless. Even if an app is being
+ // used for multiple phoneIds, we assume that it's smart enough to handle that on its own,
+ // and that in most cases we'd just be dumping duplicate information and bloating a report.
+ indentPW.increaseIndent();
+ indentPW.println(prefix + " : " + targetPkgName);
+ Set<String> dumpedPkgNames = new ArraySet<>(mServiceConnection.length);
+ for (CarrierServiceConnection connection : mServiceConnection) {
+ if (connection == null || !SubscriptionManager.isValidPhoneId(connection.phoneId)
+ || TextUtils.isEmpty(connection.pkgName)) {
+ continue;
+ }
+ final String servicePkgName = connection.pkgName;
+ // Note: we intentionally ignore system components here because we should NOT match the
+ // shell caller that's typically used for bug reports via non-BugreportManager triggers.
+ final boolean exactPackageMatch = TextUtils.equals(targetPkgName, servicePkgName);
+ final boolean carrierPrivilegesMatch =
+ considerCarrierPrivileges && hasCarrierPrivileges(targetPkgName,
+ connection.phoneId);
+ if (!exactPackageMatch && !carrierPrivilegesMatch) continue;
+ // Make sure this service is actually alive before trying to dump it. We don't pay
+ // attention to mServiceBound[connection.phoneId] because typically carrier apps will
+ // request long-lived bindings, and even if we unbind the app, it may still be alive due
+ // to CarrierServiceBindHelper. Pull it out as a reference so even if it gets set to
+ // null within the ServiceConnection during unbinding we can avoid an NPE.
+ final IBinder service = connection.service;
+ if (service == null || !service.isBinderAlive() || !service.pingBinder()) continue;
+ // We've got a live service. Last check is just to make sure we don't dump a package
+ // multiple times.
+ if (!dumpedPkgNames.add(servicePkgName)) continue;
+ if (!exactPackageMatch) {
+ logd(targetPkgName + " has carrier privileges on phoneId " + connection.phoneId
+ + ", service provided by " + servicePkgName);
+ indentPW.increaseIndent();
+ indentPW.println("Proxy : " + servicePkgName);
+ indentPW.decreaseIndent();
+ }
+ // Flush before we let the app output anything to ensure correct ordering of output.
+ // Internally, Binder#dump calls flush on its printer after finishing so we don't
+ // need to do anything after.
+ indentPW.flush();
+ try {
+ logd("Dumping " + servicePkgName);
+ // We don't need to give the carrier service any args.
+ connection.service.dump(fd, null /* args */);
+ logd("Done with " + servicePkgName);
+ } catch (RemoteException e) {
+ logd("RemoteException from " + servicePkgName, e);
+ indentPW.increaseIndent();
+ indentPW.println("RemoteException");
+ indentPW.increaseIndent();
+ e.printStackTrace(indentPW);
+ indentPW.decreaseIndent();
+ indentPW.decreaseIndent();
+ // We won't retry this package again because now it's in dumpedPkgNames.
+ }
+ indentPW.println("");
+ }
+ if (dumpedPkgNames.isEmpty()) {
+ indentPW.increaseIndent();
+ indentPW.println("Not bound");
+ indentPW.decreaseIndent();
+ indentPW.println("");
+ }
+ indentPW.decreaseIndent();
+ }
+
+ private boolean hasCarrierPrivileges(String pkgName, int phoneId) {
+ int[] subIds = SubscriptionManager.getSubId(phoneId);
+ if (ArrayUtils.isEmpty(subIds)) {
+ return false;
+ }
+ return TelephonyManager.from(mContext).createForSubscriptionId(
+ subIds[0]).checkCarrierPrivilegesForPackage(pkgName)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
}
private class CarrierServiceConnection implements ServiceConnection {
- int phoneId;
- int eventId;
+ final int phoneId;
+ final String pkgName;
+ final int eventId;
IBinder service;
- public CarrierServiceConnection(int phoneId, int eventId) {
+ CarrierServiceConnection(int phoneId, String pkgName, int eventId) {
this.phoneId = phoneId;
+ this.pkgName = pkgName;
this.eventId = eventId;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- log("Connected to config app: " + name.flattenToString());
+ logd("Connected to config app: " + name.flattenToShortString());
this.service = service;
mHandler.sendMessage(mHandler.obtainMessage(eventId, phoneId, -1, this));
}
@Override
public void onServiceDisconnected(ComponentName name) {
- log("Disconnected from config app: " + name.flattenToString());
+ logd("Disconnected from config app: " + name.flattenToShortString());
this.service = null;
}
@Override
public void onBindingDied(ComponentName name) {
- log("Binding died from config app: " + name.flattenToString());
+ logd("Binding died from config app: " + name.flattenToShortString());
this.service = null;
}
@Override
public void onNullBinding(ComponentName name) {
- log("Null binding from config app: " + name.flattenToString());
+ logd("Null binding from config app: " + name.flattenToShortString());
this.service = null;
}
}
@@ -1188,8 +1332,9 @@
String action = intent.getAction();
boolean replace = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
// If replace is true, only care ACTION_PACKAGE_REPLACED.
- if (replace && !Intent.ACTION_PACKAGE_REPLACED.equals(action))
+ if (replace && !Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
return;
+ }
switch (action) {
case Intent.ACTION_BOOT_COMPLETED:
@@ -1211,11 +1356,15 @@
}
}
- private void log(String msg) {
+ private void logd(String msg) {
Log.d(LOG_TAG, msg);
}
- private void logWithLocalLog(String msg) {
+ private void logd(String msg, Throwable tr) {
+ Log.d(LOG_TAG, msg, tr);
+ }
+
+ private void logdWithLocalLog(String msg) {
Log.d(LOG_TAG, msg);
mCarrierConfigLoadingLog.log(msg);
}
diff --git a/src/com/android/phone/CdmaCallOptions.java b/src/com/android/phone/CdmaCallOptions.java
index 8f4fa67..2e310aa 100644
--- a/src/com/android/phone/CdmaCallOptions.java
+++ b/src/com/android/phone/CdmaCallOptions.java
@@ -19,9 +19,7 @@
import android.os.Bundle;
import android.os.PersistableBundle;
import android.preference.Preference;
-import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
-import android.preference.SwitchPreference;
import android.telephony.CarrierConfigManager;
import android.view.MenuItem;
@@ -34,7 +32,7 @@
private static final String BUTTON_VP_KEY = "button_voice_privacy_key";
private static final String CALL_FORWARDING_KEY = "call_forwarding_key";
private static final String CALL_WAITING_KEY = "call_waiting_key";
- private SwitchPreference mButtonVoicePrivacy;
+ private CdmaVoicePrivacySwitchPreference mButtonVoicePrivacy;
@Override
protected void onCreate(Bundle icicle) {
@@ -46,7 +44,8 @@
subInfoHelper.setActionBarTitle(
getActionBar(), getResources(), R.string.labelCdmaMore_with_label);
- mButtonVoicePrivacy = (SwitchPreference) findPreference(BUTTON_VP_KEY);
+ mButtonVoicePrivacy = (CdmaVoicePrivacySwitchPreference) findPreference(BUTTON_VP_KEY);
+ mButtonVoicePrivacy.setPhone(subInfoHelper.getPhone());
PersistableBundle carrierConfig;
if (subInfoHelper.hasSubId()) {
carrierConfig = PhoneGlobals.getInstance().getCarrierConfigForSubId(
diff --git a/src/com/android/phone/CdmaVoicePrivacySwitchPreference.java b/src/com/android/phone/CdmaVoicePrivacySwitchPreference.java
index b79a3f0..0192c2a 100644
--- a/src/com/android/phone/CdmaVoicePrivacySwitchPreference.java
+++ b/src/com/android/phone/CdmaVoicePrivacySwitchPreference.java
@@ -102,4 +102,8 @@
phone.getEnhancedVoicePrivacy(obtainMessage(MESSAGE_GET_VP));
}
}
+
+ public void setPhone(Phone phone) {
+ this.phone = phone;
+ }
}
diff --git a/src/com/android/phone/EmergencyCallbackModeExitDialog.java b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
index 6edc155..1210627 100644
--- a/src/com/android/phone/EmergencyCallbackModeExitDialog.java
+++ b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
@@ -35,6 +35,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.internal.telephony.Phone;
@@ -330,7 +331,8 @@
// Received exit Emergency Callback Mode notification close all dialogs
if (intent.getAction().equals(
TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
- if (intent.getBooleanExtra("phoneinECMState", false) == false) {
+ // Cancel if the sticky broadcast extra for whether or not we are in ECM is false.
+ if (!intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false)) {
if (mAlertDialog != null)
mAlertDialog.dismiss();
if (mProgressDialog != null)
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index 334442f..183742e 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -40,7 +40,6 @@
import android.os.Bundle;
import android.os.PersistableBundle;
import android.provider.Settings;
-import android.telecom.ParcelableCallAnalytics;
import android.telecom.PhoneAccount;
import android.telecom.TelecomManager;
import android.telephony.CarrierConfigManager;
@@ -490,7 +489,7 @@
if (!TextUtils.isEmpty(phoneNumber)) {
if (DBG) Log.d(LOG_TAG, "dial emergency number: " + Rlog.pii(LOG_TAG, phoneNumber));
- placeCall(phoneNumber, ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_SHORTCUT,
+ placeCall(phoneNumber, TelecomManager.CALL_SOURCE_EMERGENCY_SHORTCUT,
mShortcutViewConfig.getPhoneInfo());
} else {
Log.d(LOG_TAG, "emergency number is empty");
@@ -738,7 +737,7 @@
return;
}
- placeCall(mLastNumber, ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_DIALPAD,
+ placeCall(mLastNumber, TelecomManager.CALL_SOURCE_EMERGENCY_DIALPAD,
phoneToMakeCall);
} else {
if (DBG) Log.d(LOG_TAG, "rejecting bad requested number " + mLastNumber);
@@ -1176,9 +1175,9 @@
private String callSourceToString(int callSource) {
switch (callSource) {
- case ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_DIALPAD:
+ case TelecomManager.CALL_SOURCE_EMERGENCY_DIALPAD:
return "DialPad";
- case ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_SHORTCUT:
+ case TelecomManager.CALL_SOURCE_EMERGENCY_SHORTCUT:
return "Shortcut";
default:
return "Unknown-" + callSource;
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index 3ea8df2..6a059e4 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -36,6 +36,7 @@
import com.android.ims.ImsManager;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.services.telephony.rcs.RcsFeatureController;
import com.android.services.telephony.rcs.TelephonyRcsService;
@@ -245,9 +246,13 @@
}
@Override
- public void requestCapabilities(int subId, List<Uri> contactNumbers,
- IRcsUceControllerCallback c) {
+ public void requestCapabilities(int subId, String callingPackage, String callingFeatureId,
+ List<Uri> contactNumbers, IRcsUceControllerCallback c) {
enforceReadPrivilegedPermission("requestCapabilities");
+ if (!isUceSettingEnabled(subId, callingPackage, callingFeatureId)) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "The user has not enabled UCE for this subscription.");
+ }
final long token = Binder.clearCallingIdentity();
try {
UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
@@ -280,17 +285,32 @@
}
@Override
- public boolean isUceSettingEnabled(int subId) {
- enforceReadPrivilegedPermission("isUceSettingEnabled");
- return SubscriptionManager.getBooleanSubscriptionProperty(subId,
- SubscriptionManager.IMS_RCS_UCE_ENABLED, false /*defaultValue*/, mApp);
+ public boolean isUceSettingEnabled(int subId, String callingPackage, String callingFeatureId) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, subId, callingPackage, callingFeatureId, "isUceSettingEnabled")) {
+ Log.w(TAG, "isUceSettingEnabled: READ_PHONE_STATE app op disabled when accessing "
+ + "isUceSettingEnabled");
+ return false;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return SubscriptionManager.getBooleanSubscriptionProperty(subId,
+ SubscriptionManager.IMS_RCS_UCE_ENABLED, false /*defaultValue*/, mApp);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
public void setUceSettingEnabled(int subId, boolean isEnabled) {
enforceModifyPermission();
- SubscriptionManager.setSubscriptionProperty(subId, SubscriptionManager.IMS_RCS_UCE_ENABLED,
- (isEnabled ? "1" : "0"));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ SubscriptionManager.setSubscriptionProperty(subId,
+ SubscriptionManager.IMS_RCS_UCE_ENABLED, (isEnabled ? "1" : "0"));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 92b063a..3901ac4 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -3044,14 +3044,15 @@
}
@Override
- public void sendVisualVoicemailSmsForSubscriber(String callingPackage, int subId,
- String number, int port, String text, PendingIntent sentIntent) {
+ public void sendVisualVoicemailSmsForSubscriber(String callingPackage,
+ String callingAttributionTag, int subId, String number, int port, String text,
+ PendingIntent sentIntent) {
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
enforceVisualVoicemailPackage(callingPackage, subId);
enforceSendSmsPermission();
SmsController smsController = PhoneFactory.getSmsController();
- smsController.sendVisualVoicemailSmsForSubscriber(callingPackage, subId, number, port, text,
- sentIntent);
+ smsController.sendVisualVoicemailSmsForSubscriber(callingPackage, callingAttributionTag,
+ subId, number, port, text, sentIntent);
}
/**
@@ -3435,6 +3436,9 @@
Phone phone = getPhone(subId);
if (phone == null) return false;
return phone.isImsCapabilityAvailable(capability, regTech);
+ } catch (com.android.ims.ImsException e) {
+ Log.w(LOG_TAG, "IMS isAvailable - service unavailable: " + e.getMessage());
+ return false;
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -8199,13 +8203,15 @@
}
@Override
- public void enqueueSmsPickResult(String callingPackage, IIntegerConsumer pendingSubIdResult) {
+ public void enqueueSmsPickResult(String callingPackage, String callingAttributionTag,
+ IIntegerConsumer pendingSubIdResult) {
if (callingPackage == null) {
callingPackage = getCurrentPackageName();
}
SmsPermissions permissions = new SmsPermissions(getDefaultPhone(), mApp,
(AppOpsManager) mApp.getSystemService(Context.APP_OPS_SERVICE));
- if (!permissions.checkCallingCanSendSms(callingPackage, "Sending message")) {
+ if (!permissions.checkCallingCanSendSms(callingPackage, callingAttributionTag,
+ "Sending message")) {
throw new SecurityException("Requires SEND_SMS permission to perform this operation");
}
PickSmsSubscriptionActivity.addPendingResult(pendingSubIdResult);
diff --git a/src/com/android/phone/settings/AccessibilitySettingsFragment.java b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
index e31de64..37212cf 100644
--- a/src/com/android/phone/settings/AccessibilitySettingsFragment.java
+++ b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
@@ -25,6 +25,7 @@
import android.preference.PreferenceScreen;
import android.preference.SwitchPreference;
import android.provider.Settings;
+import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
@@ -39,6 +40,11 @@
import com.android.phone.PhoneGlobals;
import com.android.phone.R;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
public class AccessibilitySettingsFragment extends PreferenceFragment {
private static final String LOG_TAG = AccessibilitySettingsFragment.class.getSimpleName();
private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
@@ -48,6 +54,8 @@
private static final String BUTTON_RTT_KEY = "button_rtt_key";
private static final String RTT_INFO_PREF = "button_rtt_more_information_key";
+ private static final int WFC_QUERY_TIMEOUT_MILLIS = 20;
+
private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
/**
* Disable the TTY setting when in/out of a call (and if carrier doesn't
@@ -110,10 +118,13 @@
}
if (shouldShowRttSetting()) {
- // TODO: this is going to be a on/off switch for now. Ask UX about how to integrate
- // this settings with TTY
- if (TelephonyManager.getDefault().isNetworkRoaming(
- SubscriptionManager.getDefaultVoiceSubscriptionId())) {
+ TelephonyManager tm =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ boolean isRoaming = tm.isNetworkRoaming(
+ SubscriptionManager.getDefaultVoiceSubscriptionId());
+
+ boolean shouldDisableBecauseRoamingOffWfc = isRoaming && !isOnWfc();
+ if (shouldDisableBecauseRoamingOffWfc) {
mButtonRtt.setSummary(TextUtils.concat(getText(R.string.rtt_mode_summary), "\n",
getText(R.string.no_rtt_when_roaming)));
}
@@ -230,6 +241,21 @@
return false;
}
+ private boolean isOnWfc() {
+ LinkedBlockingQueue<Integer> result = new LinkedBlockingQueue<>(1);
+ Executor executor = Executors.newSingleThreadExecutor();
+ mContext.getSystemService(android.telephony.ims.ImsManager.class)
+ .getImsMmTelManager(SubscriptionManager.getDefaultSubscriptionId())
+ .getRegistrationTransportType(executor, result::offer);
+ try {
+ Integer transportType = result.poll(WFC_QUERY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ return transportType != null
+ && transportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN;
+ } catch (InterruptedException e) {
+ return false;
+ }
+ }
+
private boolean shouldShowRttSetting() {
// Go through all the subs -- if we want to display the RTT setting for any of them, do
// display it.
diff --git a/src/com/android/services/telephony/RadioOnStateListener.java b/src/com/android/services/telephony/RadioOnStateListener.java
index 1bc6969..93e1e3c 100644
--- a/src/com/android/services/telephony/RadioOnStateListener.java
+++ b/src/com/android/services/telephony/RadioOnStateListener.java
@@ -172,6 +172,7 @@
* call with {@link Callback#isOkToCall}
*/
private void onServiceStateChanged(ServiceState state) {
+ if (mPhone == null) return;
Log.d(this, "onServiceStateChanged(), new state = %s, Phone = %s", state,
mPhone.getPhoneId());
@@ -195,6 +196,7 @@
}
private void onRadioOn() {
+ if (mPhone == null) return;
ServiceState state = mPhone.getServiceState();
Log.d(this, "onRadioOn, state = %s, Phone = %s", state,
mPhone.getPhoneId());
@@ -216,6 +218,7 @@
* Handles the retry timer expiring.
*/
private void onRetryTimeout() {
+ if (mPhone == null) return;
int serviceState = mPhone.getServiceState().getState();
Log.d(this, "onRetryTimeout(): phone state = %s, service state = %d, retries = %d.",
mPhone.getState(), serviceState, mNumRetriesSoFar);
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 1013927..9e880a5 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -36,6 +36,7 @@
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.provider.Settings;
+import android.provider.Telephony;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
@@ -48,6 +49,7 @@
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsRcsManager;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.RegistrationManager;
import android.telephony.ims.feature.MmTelFeature;
@@ -55,6 +57,7 @@
import android.text.TextUtils;
import com.android.ims.ImsManager;
+import com.android.internal.telephony.LocaleTracker;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.SubscriptionController;
@@ -67,6 +70,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
+import java.util.function.Predicate;
/**
* Owns all data we have registered with Telecom including handling dynamic addition and
@@ -540,16 +544,31 @@
}
/**
- * Determines from carrier configuration whether RCS presence indication for video calls is
- * supported.
+ * Determines from carrier configuration and user setting whether RCS presence indication
+ * for video calls is supported.
*
* @return {@code true} if RCS presence indication for video calls is supported.
*/
private boolean isCarrierVideoPresenceSupported() {
PersistableBundle b =
PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
- return b != null &&
- b.getBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL);
+ boolean carrierConfigEnabled = b != null
+ && b.getBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL);
+ return carrierConfigEnabled && isUserContactDiscoverySettingEnabled();
+ }
+
+ /**
+ * @return true if the user has enabled contact discovery for the subscription associated
+ * with this account entry, false otherwise.
+ */
+ private boolean isUserContactDiscoverySettingEnabled() {
+ try {
+ ImsRcsManager manager = mImsManager.getImsRcsManager(mPhone.getSubId());
+ return manager.getUceAdapter().isUceSettingEnabled();
+ } catch (Exception e) {
+ Log.w(LOG_TAG, "isUserContactDiscoverySettingEnabled caught exception: " + e);
+ return false;
+ }
}
/**
@@ -751,6 +770,25 @@
}
}
+ public void updateVideoPresenceCapability() {
+ synchronized (mAccountsLock) {
+ if (!mAccounts.contains(this)) {
+ // Account has already been torn down, don't try to register it again.
+ // This handles the case where teardown has already happened, and we got a Ims
+ // registration update that lost the race for the mAccountsLock. In such a
+ // scenario by the time we get here, the original phone account could have been
+ // torn down.
+ return;
+ }
+ boolean isVideoPresenceSupported = isCarrierVideoPresenceSupported();
+ if (mIsVideoPresenceSupported != isVideoPresenceSupported) {
+ Log.i(this, "updateVideoPresenceCapability for subId=" + mPhone.getSubId()
+ + ", new value= " + isVideoPresenceSupported);
+ mAccount = registerPstnPhoneAccount(mIsEmergency, mIsDummy);
+ }
+ }
+ }
+
public void updateRttCapability() {
boolean isRttEnabled = isRttCurrentlySupported();
if (isRttEnabled != mIsRttCapable) {
@@ -773,14 +811,51 @@
* device.
*/
private boolean isRttCurrentlySupported() {
+ // First check the emergency case -- if it's supported and turned on,
+ // we want to present RTT as available on the emergency-only phone account
+ if (mIsEmergency) {
+ // First check whether the device supports it
+ boolean devicesSupportsRtt =
+ mContext.getResources().getBoolean(R.bool.config_support_rtt);
+ boolean deviceSupportsEmergencyRtt = mContext.getResources().getBoolean(
+ R.bool.config_support_simless_emergency_rtt);
+ if (!(deviceSupportsEmergencyRtt && devicesSupportsRtt)) {
+ Log.i(this, "isRttCurrentlySupported -- emergency acct and no device support");
+ return false;
+ }
+ // Next check whether we're in or near a country that supports it
+ String country =
+ mPhone.getServiceStateTracker().getLocaleTracker()
+ .getCurrentCountry().toLowerCase();
+ String[] supportedCountries = mContext.getResources().getStringArray(
+ R.array.config_simless_emergency_rtt_supported_countries);
+ if (supportedCountries == null || Arrays.stream(supportedCountries).noneMatch(
+ Predicate.isEqual(country))) {
+ Log.i(this, "isRttCurrentlySupported -- emergency acct and"
+ + " not supported in this country: " + country);
+ return false;
+ }
+
+ return true;
+ }
+
boolean hasVoiceAvailability = isImsVoiceAvailable();
boolean isRttSupported = PhoneGlobals.getInstance().phoneMgr
.isRttEnabled(mPhone.getSubId());
boolean isRoaming = mTelephonyManager.isNetworkRoaming(mPhone.getSubId());
+ boolean isOnWfc = mPhone.getImsRegistrationTech()
+ == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
- return hasVoiceAvailability && isRttSupported && !isRoaming;
+ boolean shouldDisableBecauseRoamingOffWfc = isRoaming && !isOnWfc;
+ Log.i(this, "isRttCurrentlySupported -- regular acct,"
+ + " hasVoiceAvailability: " + hasVoiceAvailability + "\n"
+ + " isRttSupported: " + isRttSupported + "\n"
+ + " isRoaming: " + isRoaming + "\n"
+ + " isOnWfc: " + isOnWfc + "\n");
+
+ return hasVoiceAvailability && isRttSupported && !shouldDisableBecauseRoamingOffWfc;
}
/**
@@ -947,6 +1022,7 @@
private static TelecomAccountRegistry sInstance;
private final Context mContext;
private final TelecomManager mTelecomManager;
+ private final android.telephony.ims.ImsManager mImsManager;
private final TelephonyManager mTelephonyManager;
private final SubscriptionManager mSubscriptionManager;
private List<AccountEntry> mAccounts = new LinkedList<AccountEntry>();
@@ -962,6 +1038,7 @@
TelecomAccountRegistry(Context context) {
mContext = context;
mTelecomManager = context.getSystemService(TelecomManager.class);
+ mImsManager = context.getSystemService(android.telephony.ims.ImsManager.class);
mTelephonyManager = TelephonyManager.from(context);
mSubscriptionManager = SubscriptionManager.from(context);
}
@@ -1196,9 +1273,14 @@
//We also need to listen for locale changes
//(e.g. system language changed -> SIM card name changed)
- mContext.registerReceiver(mLocaleChangeReceiver,
- new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
+ IntentFilter localeChangeFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
+ localeChangeFilter.addAction(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED);
+ mContext.registerReceiver(mLocaleChangeReceiver, localeChangeFilter);
+ registerContentObservers();
+ }
+
+ private void registerContentObservers() {
// Listen to the RTT system setting so that we update it when the user flips it.
ContentObserver rttUiSettingObserver = new ContentObserver(
new Handler(Looper.getMainLooper())) {
@@ -1215,6 +1297,23 @@
Uri rttSettingUri = Settings.Secure.getUriFor(Settings.Secure.RTT_CALLING_MODE);
mContext.getContentResolver().registerContentObserver(
rttSettingUri, false, rttUiSettingObserver);
+
+ // Listen to the changes to the user's Contacts Discovery Setting.
+ ContentObserver contactDiscoveryObserver = new ContentObserver(
+ new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mAccountsLock) {
+ for (AccountEntry account : mAccounts) {
+ account.updateVideoPresenceCapability();
+ }
+ }
+ }
+ };
+ Uri contactDiscUri = Uri.withAppendedPath(Telephony.SimInfo.CONTENT_URI,
+ Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED);
+ mContext.getContentResolver().registerContentObserver(
+ contactDiscUri, true /*notifyForDescendants*/, contactDiscoveryObserver);
}
/**
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 665bf22..eb6482f 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -2176,30 +2176,24 @@
return 1;
}
// sort by number of RadioAccessFamily Capabilities.
- int compare = Integer.bitCount(o1.capabilities) -
- Integer.bitCount(o2.capabilities);
+ int compare = RadioAccessFamily.compare(o1.capabilities, o2.capabilities);
if (compare == 0) {
- // Sort by highest RAF Capability if the number is the same.
- compare = RadioAccessFamily.getHighestRafCapability(o1.capabilities) -
- RadioAccessFamily.getHighestRafCapability(o2.capabilities);
- if (compare == 0) {
- if (firstOccupiedSlot != null) {
- // If the RAF capability is the same, choose based on whether or
- // not any of the slots are occupied with a SIM card (if both
- // are, always choose the first).
- if (o1.slotId == firstOccupiedSlot.getPhoneId()) {
- return 1;
- } else if (o2.slotId == firstOccupiedSlot.getPhoneId()) {
- return -1;
- }
- } else {
- // No slots have SIMs detected in them, so weight the default
- // Phone Id greater than the others.
- if (o1.slotId == defaultPhoneId) {
- return 1;
- } else if (o2.slotId == defaultPhoneId) {
- return -1;
- }
+ if (firstOccupiedSlot != null) {
+ // If the RAF capability is the same, choose based on whether or
+ // not any of the slots are occupied with a SIM card (if both
+ // are, always choose the first).
+ if (o1.slotId == firstOccupiedSlot.getPhoneId()) {
+ return 1;
+ } else if (o2.slotId == firstOccupiedSlot.getPhoneId()) {
+ return -1;
+ }
+ } else {
+ // No slots have SIMs detected in them, so weight the default
+ // Phone Id greater than the others.
+ if (o1.slotId == defaultPhoneId) {
+ return 1;
+ } else if (o2.slotId == defaultPhoneId) {
+ return -1;
}
}
}
diff --git a/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java b/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
index dffbbf3..d55a2fa 100644
--- a/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
+++ b/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
@@ -69,9 +69,11 @@
@Override
@After
public void tearDown() throws Exception {
+ mListener.setTimeBetweenRetriesMillis(5000);
+ mListener.setMaxNumRetries(5);
+ mListener.getHandler().removeCallbacksAndMessages(null);
// Wait for the queue to clear...
waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS /*ms timeout*/);
- mListener.getHandler().removeCallbacksAndMessages(null);
mListener = null;
super.tearDown();
}