diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index d1d5d8e460e80..673da507788f1 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -165,6 +165,11 @@ public class ZygoteProcess { */ private List mApiBlacklistExemptions = Collections.emptyList(); + /** + * Proportion of hidden API accesses that should be logged to the event log; 0 - 0x10000. + */ + private int mHiddenApiAccessLogSampleRate; + /** * The state of the connection to the primary zygote. */ @@ -478,6 +483,21 @@ public class ZygoteProcess { } } + /** + * Set the precentage of detected hidden API accesses that are logged to the event log. + * + *

This rate will take affect for all new processes forked from the zygote after this call. + * + * @param rate An integer between 0 and 0x10000 inclusive. 0 means no event logging. + */ + public void setHiddenApiAccessLogSampleRate(int rate) { + synchronized (mLock) { + mHiddenApiAccessLogSampleRate = rate; + maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState); + maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState); + } + } + @GuardedBy("mLock") private void maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) { if (state == null || state.isClosed()) { @@ -505,6 +525,29 @@ public class ZygoteProcess { } } + private void maybeSetHiddenApiAccessLogSampleRate(ZygoteState state) { + if (state == null || state.isClosed()) { + return; + } + if (mHiddenApiAccessLogSampleRate == -1) { + return; + } + try { + state.writer.write(Integer.toString(1)); + state.writer.newLine(); + state.writer.write("--hidden-api-log-sampling-rate=" + + Integer.toString(mHiddenApiAccessLogSampleRate)); + state.writer.newLine(); + state.writer.flush(); + int status = state.inputStream.readInt(); + if (status != 0) { + Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate; status " + status); + } + } catch (IOException ioe) { + Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate", 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. @@ -520,6 +563,7 @@ public class ZygoteProcess { throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe); } maybeSetApiBlacklistExemptions(primaryZygoteState, false); + maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState); } if (primaryZygoteState.matches(abi)) { return primaryZygoteState; @@ -533,6 +577,7 @@ public class ZygoteProcess { throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe); } maybeSetApiBlacklistExemptions(secondaryZygoteState, false); + maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState); } if (secondaryZygoteState.matches(abi)) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e3029b62833da..f1f5a507e9f59 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11773,6 +11773,15 @@ public final class Settings { public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions"; + /** + * Sampling rate for hidden API access event logs, as an integer in the range 0 to 0x10000 + * inclusive. + * + * @hide + */ + public static final String HIDDEN_API_ACCESS_LOG_SAMPLING_RATE = + "hidden_api_access_log_sampling_rate"; + /** * Hidden API enforcement policy for apps targeting SDK versions prior to the latest * version. diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 5d40a7300919a..f537e3e2897b8 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -166,6 +166,11 @@ class ZygoteConnection { return null; } + if (parsedArgs.hiddenApiAccessLogSampleRate != -1) { + handleHiddenApiAccessLogSampleRate(parsedArgs.hiddenApiAccessLogSampleRate); + return null; + } + if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) { throw new ZygoteSecurityException("Client may not specify capabilities: " + "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) + @@ -294,6 +299,15 @@ class ZygoteConnection { } } + private void handleHiddenApiAccessLogSampleRate(int percent) { + try { + ZygoteInit.setHiddenApiAccessLogSampleRate(percent); + mSocketOutStream.writeInt(0); + } catch (IOException ioe) { + throw new IllegalStateException("Error writing to command socket", ioe); + } + } + protected void preload() { ZygoteInit.lazyPreload(); } @@ -460,6 +474,12 @@ class ZygoteConnection { */ String[] apiBlacklistExemptions; + /** + * Sampling rate for logging hidden API accesses to the event log. This is sent to the + * pre-forked zygote at boot time, or when it changes, via --hidden-api-log-sampling-rate. + */ + int hiddenApiAccessLogSampleRate = -1; + /** * Constructs instance and parses args * @param args zygote command-line args @@ -483,6 +503,7 @@ class ZygoteConnection { boolean seenRuntimeArgs = false; + boolean expectRuntimeArgs = true; for ( /* curArg */ ; curArg < args.length; curArg++) { String arg = args[curArg]; @@ -612,6 +633,7 @@ class ZygoteConnection { preloadPackageCacheKey = args[++curArg]; } else if (arg.equals("--preload-default")) { preloadDefault = true; + expectRuntimeArgs = false; } else if (arg.equals("--start-child-zygote")) { startChildZygote = true; } else if (arg.equals("--set-api-blacklist-exemptions")) { @@ -619,6 +641,16 @@ class ZygoteConnection { // with the regular fork command. apiBlacklistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length); curArg = args.length; + expectRuntimeArgs = false; + } else if (arg.startsWith("--hidden-api-log-sampling-rate=")) { + String rateStr = arg.substring(arg.indexOf('=') + 1); + try { + hiddenApiAccessLogSampleRate = Integer.parseInt(rateStr); + } catch (NumberFormatException nfe) { + throw new IllegalArgumentException( + "Invalid log sampling rate: " + rateStr, nfe); + } + expectRuntimeArgs = false; } else { break; } @@ -633,7 +665,7 @@ class ZygoteConnection { throw new IllegalArgumentException( "Unexpected arguments after --preload-package."); } - } else if (!preloadDefault && apiBlacklistExemptions == null) { + } else if (expectRuntimeArgs) { 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 c5d41db934da5..6f5836531c271 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -26,8 +26,8 @@ import android.icu.text.DecimalFormatSymbols; import android.icu.util.ULocale; import android.opengl.EGL14; import android.os.Build; -import android.os.IInstalld; import android.os.Environment; +import android.os.IInstalld; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -44,16 +44,16 @@ import android.system.OsConstants; import android.system.StructCapUserData; import android.system.StructCapUserHeader; import android.text.Hyphenator; -import android.util.TimingsTraceLog; import android.util.EventLog; import android.util.Log; import android.util.Slog; +import android.util.TimingsTraceLog; import android.webkit.WebViewFactory; import android.widget.TextView; import com.android.internal.logging.MetricsLogger; - import com.android.internal.util.Preconditions; + import dalvik.system.DexFile; import dalvik.system.VMRuntime; import dalvik.system.ZygoteHooks; @@ -67,8 +67,8 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.security.Security; import java.security.Provider; +import java.security.Security; /** * Startup class for the zygote process. @@ -518,6 +518,10 @@ public class ZygoteInit { VMRuntime.getRuntime().setHiddenApiExemptions(exemptions); } + public static void setHiddenApiAccessLogSampleRate(int percent) { + VMRuntime.getRuntime().setHiddenApiAccessLogSamplingRate(percent); + } + /** * 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/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index b150a796beafe..96ac5895a281f 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -247,6 +247,7 @@ public class SettingsBackupTest { Settings.Global.HDMI_CONTROL_ENABLED, Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, + Settings.Global.HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, Settings.Global.HIDDEN_API_POLICY_P_APPS, Settings.Global.HIDDEN_API_POLICY_PRE_P_APPS, Settings.Global.HIDE_ERROR_DIALOGS, diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 5497e624a09cc..022f8655ada06 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2902,6 +2902,7 @@ public class ActivityManagerService extends IActivityManager.Stub private boolean mBlacklistDisabled; private String mExemptionsStr; private List mExemptions = Collections.emptyList(); + private int mLogSampleRate = -1; @HiddenApiEnforcementPolicy private int mPolicyPreP = HIDDEN_API_ENFORCEMENT_DEFAULT; @HiddenApiEnforcementPolicy private int mPolicyP = HIDDEN_API_ENFORCEMENT_DEFAULT; @@ -2915,6 +2916,10 @@ public class ActivityManagerService extends IActivityManager.Stub Settings.Global.getUriFor(Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS), false, this); + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.HIDDEN_API_ACCESS_LOG_SAMPLING_RATE), + false, + this); mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.HIDDEN_API_POLICY_PRE_P_APPS), false, @@ -2942,6 +2947,15 @@ public class ActivityManagerService extends IActivityManager.Stub } zygoteProcess.setApiBlacklistExemptions(mExemptions); } + int logSampleRate = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, -1); + if (logSampleRate < 0 || logSampleRate > 0x10000) { + logSampleRate = -1; + } + if (logSampleRate != -1 && logSampleRate != mLogSampleRate) { + mLogSampleRate = logSampleRate; + zygoteProcess.setHiddenApiAccessLogSampleRate(mLogSampleRate); + } mPolicyPreP = getValidEnforcementPolicy(Settings.Global.HIDDEN_API_POLICY_PRE_P_APPS); mPolicyP = getValidEnforcementPolicy(Settings.Global.HIDDEN_API_POLICY_P_APPS); }