summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/os/ZygoteProcess.java56
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java29
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java4
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java22
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() {