From 04194fef78ecd75ea09cb9c75c211f5170e6f91d Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Wed, 4 Apr 2018 14:48:03 +0100 Subject: [PATCH] Configurable sampling rate for hidden API access log events. To reduce log spam, we do not log all hidden API accesses. This CL allows configuration of the sampling rate of the log events so it can be tweaked later if necessary. Test: m Test: $ adb shell settings put global hidden_api_access_log_sampling_rate 65536 Bug: 64382372 Bug: 77517571 Change-Id: I659c22bd504564da58d972f94b774a9af4039386 --- core/java/android/os/ZygoteProcess.java | 45 +++++++++++++++++++ core/java/android/provider/Settings.java | 9 ++++ .../android/internal/os/ZygoteConnection.java | 34 +++++++++++++- .../com/android/internal/os/ZygoteInit.java | 12 +++-- .../android/provider/SettingsBackupTest.java | 1 + .../server/am/ActivityManagerService.java | 14 ++++++ 6 files changed, 110 insertions(+), 5 deletions(-) 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 60bbb10de0141..cbc1e3b9f8bd8 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11748,6 +11748,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 a5941b26b9d3e..558e576b71aff 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -246,6 +246,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 04d54e1dcb8dd..01580142382f8 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); }