diff options
3 files changed, 79 insertions, 1 deletions
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 57c12403dec8..9f898b823a76 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -854,4 +854,6 @@ interface IPackageManager { boolean isPageSizeCompatEnabled(in String packageName); String getPageSizeCompatWarningMessage(in String packageName); + + List<String> getAllApexDirectories(); } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 03fea37deaf5..6128d45831fb 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -130,10 +130,12 @@ import com.google.android.collect.Sets; import libcore.util.HexEncoding; +import java.io.BufferedReader; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -380,6 +382,8 @@ public class SettingsProvider extends ContentProvider { @GuardedBy("mLock") private Handler mHandler; + private static final Set<String> sDeviceConfigAllowlistedNamespaces = new ArraySet<>(); + // We have to call in the user manager with no lock held, private volatile UserManager mUserManager; @@ -2442,6 +2446,10 @@ public class SettingsProvider extends ContentProvider { if (!isRestrictedShell && hasWritePermission) { assertCallingUserDenyList(flags); } else if (hasAllowlistPermission) { + Set<String> allowlistedDeviceConfigNamespaces = null; + if (isRestrictedShell) { + allowlistedDeviceConfigNamespaces = getAllowlistedDeviceConfigNamespaces(); + } for (String flag : flags) { boolean namespaceAllowed = false; if (isRestrictedShell) { @@ -2452,7 +2460,7 @@ public class SettingsProvider extends ContentProvider { } else { flagNamespace = flag; } - if (WritableNamespaces.ALLOWLIST.contains(flagNamespace)) { + if (allowlistedDeviceConfigNamespaces.contains(flagNamespace)) { namespaceAllowed = true; } } else { @@ -2513,6 +2521,60 @@ public class SettingsProvider extends ContentProvider { } } + /** + * Returns a Set of DeviceConfig allowlisted namespaces in which all flags can be modified + * by a caller with the {@code WRITE_ALLOWLISTED_DEVICE_CONFIG} permission. + * <p> + * This method also supports mainline modules that introduce their own allowlisted + * namespaces within the {@code etc/writable_namespaces} file under their directory. + */ + private Set<String> getAllowlistedDeviceConfigNamespaces() { + synchronized (sDeviceConfigAllowlistedNamespaces) { + if (!sDeviceConfigAllowlistedNamespaces.isEmpty()) { + return sDeviceConfigAllowlistedNamespaces; + } + if (android.provider.flags.Flags.deviceConfigWritableNamespacesApi()) { + sDeviceConfigAllowlistedNamespaces.addAll(DeviceConfig.getAdbWritableNamespaces()); + } else { + sDeviceConfigAllowlistedNamespaces.addAll(WritableNamespaces.ALLOWLIST); + } + final long identity = Binder.clearCallingIdentity(); + try { + List<String> apexDirectories; + try { + apexDirectories = mPackageManager.getAllApexDirectories(); + } catch (RemoteException e) { + Slog.e(LOG_TAG, "Caught a RemoteException obtaining APEX directories: ", e); + return sDeviceConfigAllowlistedNamespaces; + } + for (int i = 0; i < apexDirectories.size(); i++) { + String apexDirectory = apexDirectories.get(i); + File namespaceFile = Environment.buildPath(new File(apexDirectory), "etc", + "writable_namespaces"); + if (namespaceFile.exists() && namespaceFile.isFile()) { + try (BufferedReader reader = new BufferedReader( + new FileReader(namespaceFile))) { + String namespace; + while ((namespace = reader.readLine()) != null) { + namespace = namespace.trim(); + // Support comments by ignoring any lines that start with '#'. + if (!namespace.isEmpty() && !namespace.startsWith("#")) { + sDeviceConfigAllowlistedNamespaces.add(namespace); + } + } + } catch (IOException e) { + Slog.e(LOG_TAG, "Caught an exception parsing file: " + namespaceFile, + e); + } + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + return sDeviceConfigAllowlistedNamespaces; + } + } + private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk( int targetSdkVersion, String name) { // If the app targets Lollipop MR1 or older SDK we warn, otherwise crash. diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 442db10ab039..040b1943b23d 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -6640,6 +6640,20 @@ public class PackageManagerService implements PackageSender, TestUtilityService } @Override + @NonNull + public List<String> getAllApexDirectories() { + PackageManagerServiceUtils.enforceSystemOrRoot( + "getAllApexDirectories can only be called by system or root"); + List<String> apexDirectories = new ArrayList<>(); + List<ApexManager.ActiveApexInfo> apexes = mApexManager.getActiveApexInfos(); + for (int i = 0; i < apexes.size(); i++) { + ApexManager.ActiveApexInfo apex = apexes.get(i); + apexDirectories.add(apex.apexDirectory.getAbsolutePath()); + } + return apexDirectories; + } + + @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { try { |