diff options
4 files changed, 105 insertions, 6 deletions
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index 57418c8b9879..939c190f1480 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -32,6 +32,7 @@ import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.UUID; @@ -158,6 +159,13 @@ public class ZygoteProcess { private final Object mLock = new Object(); /** + * List of exemptions to the API blacklist. These are prefix matches on the runtime format + * symbol signature. Any matching symbol is treated by the runtime as being on the light grey + * list. + */ + private List<String> mApiBlacklistExemptions = Collections.emptyList(); + + /** * The state of the connection to the primary zygote. */ private ZygoteState primaryZygoteState; @@ -175,7 +183,7 @@ public class ZygoteProcess { * The process will continue running after this function returns. * * <p>If processes are not enabled, a new thread in the caller's - * process is created and main() of <var>processClass</var> called there. + * process is created and main() of <var>processclass</var> called there. * * <p>The niceName parameter, if not an empty string, is a custom name to * give to the process instead of using processClass. This allows you to @@ -454,6 +462,49 @@ public class ZygoteProcess { } /** + * Push hidden API blacklisting exemptions into the zygote process(es). + * + * <p>The list of exemptions will take affect for all new processes forked from the zygote after + * this call. + * + * @param exemptions List of hidden API exemption prefixes. + */ + public void setApiBlacklistExemptions(List<String> exemptions) { + synchronized (mLock) { + mApiBlacklistExemptions = exemptions; + maybeSetApiBlacklistExemptions(primaryZygoteState, true); + maybeSetApiBlacklistExemptions(secondaryZygoteState, true); + } + } + + @GuardedBy("mLock") + private void maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) { + if (state == null || state.isClosed()) { + return; + } + if (!sendIfEmpty && mApiBlacklistExemptions.isEmpty()) { + return; + } + try { + state.writer.write(Integer.toString(mApiBlacklistExemptions.size() + 1)); + state.writer.newLine(); + state.writer.write("--set-api-blacklist-exemptions"); + state.writer.newLine(); + for (int i = 0; i < mApiBlacklistExemptions.size(); ++i) { + state.writer.write(mApiBlacklistExemptions.get(i)); + state.writer.newLine(); + } + state.writer.flush(); + int status = state.inputStream.readInt(); + if (status != 0) { + Slog.e(LOG_TAG, "Failed to set API blacklist exemptions; status " + status); + } + } catch (IOException ioe) { + Slog.e(LOG_TAG, "Failed to set API blacklist exemptions", ioe); + } + } + + /** * Tries to open socket to Zygote process if not already open. If * already open, does nothing. May block and retry. Requires that mLock be held. */ @@ -467,8 +518,8 @@ public class ZygoteProcess { } catch (IOException ioe) { throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe); } + maybeSetApiBlacklistExemptions(primaryZygoteState, false); } - if (primaryZygoteState.matches(abi)) { return primaryZygoteState; } @@ -480,6 +531,7 @@ public class ZygoteProcess { } catch (IOException ioe) { throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe); } + maybeSetApiBlacklistExemptions(secondaryZygoteState, false); } if (secondaryZygoteState.matches(abi)) { diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index a32fb4316d12..adc550866895 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -47,6 +47,8 @@ import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; + import libcore.io.IoUtils; /** @@ -159,6 +161,11 @@ class ZygoteConnection { return null; } + if (parsedArgs.apiBlacklistExemptions != null) { + handleApiBlacklistExemptions(parsedArgs.apiBlacklistExemptions); + return null; + } + if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) { throw new ZygoteSecurityException("Client may not specify capabilities: " + "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) + @@ -278,6 +285,15 @@ class ZygoteConnection { } } + private void handleApiBlacklistExemptions(String[] exemptions) { + try { + ZygoteInit.setApiBlacklistExemptions(exemptions); + mSocketOutStream.writeInt(0); + } catch (IOException ioe) { + throw new IllegalStateException("Error writing to command socket", ioe); + } + } + protected void preload() { ZygoteInit.lazyPreload(); } @@ -424,6 +440,12 @@ class ZygoteConnection { boolean startChildZygote; /** + * Exemptions from API blacklisting. These are sent to the pre-forked zygote at boot time, + * or when they change, via --set-api-blacklist-exemptions. + */ + String[] apiBlacklistExemptions; + + /** * Constructs instance and parses args * @param args zygote command-line args * @throws IllegalArgumentException @@ -576,6 +598,11 @@ class ZygoteConnection { preloadDefault = true; } else if (arg.equals("--start-child-zygote")) { startChildZygote = true; + } else if (arg.equals("--set-api-blacklist-exemptions")) { + // consume all remaining args; this is a stand-alone command, never included + // with the regular fork command. + apiBlacklistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length); + curArg = args.length; } else { break; } @@ -590,7 +617,7 @@ class ZygoteConnection { throw new IllegalArgumentException( "Unexpected arguments after --preload-package."); } - } else if (!preloadDefault) { + } else if (!preloadDefault && apiBlacklistExemptions == null) { if (!seenRuntimeArgs) { throw new IllegalArgumentException("Unexpected argument : " + args[curArg]); } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index a05454f53bf8..c8e71021956c 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -514,6 +514,10 @@ public class ZygoteInit { /* should never reach here */ } + public static void setApiBlacklistExemptions(String[] exemptions) { + VMRuntime.getRuntime().setHiddenApiExemptions(exemptions); + } + /** * Creates a PathClassLoader for the given class path that is associated with a shared * namespace, i.e., this classloader can access platform-private native libraries. The diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 24f646ae96fc..f0673c2e7dc9 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2696,13 +2696,15 @@ public class ActivityManagerService extends IActivityManager.Stub } /** - * Encapsulates the globla setting "hidden_api_blacklist_exemptions", including tracking the + * Encapsulates the global setting "hidden_api_blacklist_exemptions", including tracking the * latest value via a content observer. */ static class HiddenApiBlacklist extends ContentObserver { private final Context mContext; private boolean mBlacklistDisabled; + private String mExemptionsStr; + private List<String> mExemptions = Collections.emptyList(); public HiddenApiBlacklist(Handler handler, Context context) { super(handler); @@ -2718,8 +2720,22 @@ public class ActivityManagerService extends IActivityManager.Stub } private void update() { - mBlacklistDisabled = "*".equals(Settings.Global.getString(mContext.getContentResolver(), - Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS)); + String exemptions = Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS); + if (!TextUtils.equals(exemptions, mExemptionsStr)) { + mExemptionsStr = exemptions; + if ("*".equals(exemptions)) { + mBlacklistDisabled = true; + mExemptions = Collections.emptyList(); + } else { + mBlacklistDisabled = false; + mExemptions = TextUtils.isEmpty(exemptions) + ? Collections.emptyList() + : Arrays.asList(exemptions.split(":")); + } + zygoteProcess.setApiBlacklistExemptions(mExemptions); + } + } boolean isDisabled() { |