Carrier privileges matching for CCL#dump.
It's possible for multiple apps to hold carrier privileges, but only one
can be the active CarrierService for a SIM. If package A is the
CarrierService but package B requests the bug report and also holds
carrier privileges, we should still dump package A as an approximate
equivalent to --requesting-package (package B).
Bug: 146521742
Test: dumpsys carrier_config --requesting-package <various>
Test: trigger bug report on user build from app with carrier privileges
Change-Id: I59d0e0ba3c8185ca8bfef0b94b2ab4bed210499f
Merged-In: I59d0e0ba3c8185ca8bfef0b94b2ab4bed210499f
(cherry picked from commit fd88fbcc1bf19d81a09aedc1c4a8719f1023360f)
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 1ccfe7b..b969ce4 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -50,6 +50,7 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Log;
@@ -74,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.
@@ -1101,8 +1103,18 @@
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
return;
}
+ 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);
+ }
+
indentPW.println("CarrierConfigLoader: " + this);
- for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
+ for (int i = 0; i < TelephonyManager.from(mContext).getActiveModemCount(); i++) {
indentPW.println("Phone Id = " + i);
// display default values in CarrierConfigManager
printConfig(CarrierConfigManager.getDefaultConfig(), indentPW,
@@ -1118,20 +1130,15 @@
indentPW.println("CarrierConfigLoadingLog=");
mCarrierConfigLoadingLog.dump(fd, indentPW, args);
- int requestingPackageIndex = ArrayUtils.indexOf(args, DUMP_ARG_REQUESTING_PACKAGE);
- if (requestingPackageIndex != -1 && requestingPackageIndex < args.length - 1
- && !TextUtils.isEmpty(args[requestingPackageIndex + 1])) {
- String requestingPackage = args[requestingPackageIndex + 1];
- indentPW.println("");
- // 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);
+ 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);
- dumpCarrierServiceIfBound(fd, indentPW, "Requesting package", requestingPackage);
+ mPlatformCarrierConfigPackage, false /* considerCarrierPrivileges */);
+ dumpCarrierServiceIfBound(fd, indentPW, "Requesting package", requestingPackage,
+ true /* considerCarrierPrivileges */);
}
}
@@ -1191,63 +1198,100 @@
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 pkgName) {
+ 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 + " : " + pkgName);
+ indentPW.println(prefix + " : " + targetPkgName);
+ Set<String> dumpedPkgNames = new ArraySet<>(mServiceConnection.length);
for (CarrierServiceConnection connection : mServiceConnection) {
- try {
- // 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.
- if (connection == null || connection.service == null || !TextUtils.equals(pkgName,
- connection.pkgName) || !connection.service.isBinderAlive()
- || !connection.service.pingBinder()) {
- continue;
- }
- // 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 after the call finishes.
- indentPW.flush();
- try {
- logd("Dumping " + pkgName);
- // We don't need to give the carrier service any args.
- connection.service.dump(fd, null /* args */);
- logd("Done with " + pkgName);
- indentPW.decreaseIndent();
- indentPW.println("");
- return;
- } catch (RemoteException e) {
- indentPW.println("RemoteException");
- logd("RemoteException from " + pkgName, e);
- e.printStackTrace(indentPW);
- indentPW.decreaseIndent();
- indentPW.println("");
- return;
- }
- } catch (NullPointerException e) {
- // Highly unlikely, but possible if the carrier app was just unbound right before we
- // we tried to dump it so the binder was reset to null. Loop in case we have more
- // candidates on other phoneIds.
- logd("NullPointerException from " + pkgName, e);
+ 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("");
}
- indentPW.increaseIndent();
- indentPW.println("Not bound");
+ if (dumpedPkgNames.isEmpty()) {
+ indentPW.increaseIndent();
+ indentPW.println("Not bound");
+ indentPW.decreaseIndent();
+ indentPW.println("");
+ }
indentPW.decreaseIndent();
- indentPW.decreaseIndent();
- indentPW.println("");
+ }
+
+ 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;
- String pkgName;
- int eventId;
+ final int phoneId;
+ final String pkgName;
+ final int eventId;
IBinder service;
CarrierServiceConnection(int phoneId, String pkgName, int eventId) {