diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 2516a3e970df8..21e454f11ebcc 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5533,32 +5533,8 @@ public final class ActivityThread { View.mDebugViewAttributes = mCoreSettings.getInt(Settings.Global.DEBUG_VIEW_ATTRIBUTES, 0) != 0; - /** - * For system applications on userdebug/eng builds, log stack - * traces of disk and network access to dropbox for analysis. - */ - if ((data.appInfo.flags & - (ApplicationInfo.FLAG_SYSTEM | - ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0) { - StrictMode.conditionallyEnableDebugLogging(); - } - - /** - * For apps targetting Honeycomb or later, we don't allow network usage - * on the main event loop / UI thread. This is what ultimately throws - * {@link NetworkOnMainThreadException}. - */ - if (data.appInfo.targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) { - StrictMode.enableDeathOnNetwork(); - } - - /** - * For apps targetting N or later, we don't allow file:// Uri exposure. - * This is what ultimately throws {@link FileUriExposedException}. - */ - if (data.appInfo.targetSdkVersion >= Build.VERSION_CODES.N) { - StrictMode.enableDeathOnFileUriExposure(); - } + StrictMode.initThreadDefaults(data.appInfo); + StrictMode.initVmDefaults(data.appInfo); // We deprecated Build.SERIAL and only apps that target pre NMR1 // SDK can see it. Since access to the serial is now behind a @@ -5655,7 +5631,12 @@ public final class ActivityThread { mResourcesManager.getConfiguration().getLocales()); if (!Process.isIsolated()) { - setupGraphicsSupport(appContext); + final int oldMask = StrictMode.allowThreadDiskWritesMask(); + try { + setupGraphicsSupport(appContext); + } finally { + StrictMode.setThreadPolicyMask(oldMask); + } } // If we use profiles, setup the dex reporter to notify package manager diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 56d6e0a62f94d..7c53ec198e7d4 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -320,8 +320,17 @@ public class FileUtils { * is {@code filename}. */ public static void bytesToFile(String filename, byte[] content) throws IOException { - try (FileOutputStream fos = new FileOutputStream(filename)) { - fos.write(content); + if (filename.startsWith("/proc/")) { + final int oldMask = StrictMode.allowThreadDiskWritesMask(); + try (FileOutputStream fos = new FileOutputStream(filename)) { + fos.write(content); + } finally { + StrictMode.setThreadPolicyMask(oldMask); + } + } else { + try (FileOutputStream fos = new FileOutputStream(filename)) { + fos.write(content); + } } } diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 407846fe6c6c2..597b8b997fa4f 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -20,11 +20,13 @@ import android.annotation.Nullable; import android.annotation.TestApi; import android.app.ActivityManager; import android.app.ActivityThread; +import android.app.ApplicationErrorReport; import android.app.IActivityManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; import android.net.TrafficStats; import android.net.Uri; import android.os.strictmode.CleartextNetworkViolation; @@ -157,6 +159,13 @@ public final class StrictMode { */ private static final String CLEARTEXT_PROPERTY = "persist.sys.strictmode.clear"; + /** + * Quick feature-flag that can be used to disable the defaults provided by + * {@link #initThreadDefaults(ApplicationInfo)} and + * {@link #initVmDefaults(ApplicationInfo)}. + */ + private static final boolean DISABLE = true; + // Only log a duplicate stack trace to the logs every second. private static final long MIN_LOG_INTERVAL_MS = 1000; @@ -713,6 +722,11 @@ public final class StrictMode { return enable(DETECT_VM_ACTIVITY_LEAKS); } + /** @hide */ + public Builder permitActivityLeaks() { + return disable(DETECT_VM_ACTIVITY_LEAKS); + } + /** * Detect everything that's potentially suspect. * @@ -846,6 +860,11 @@ public final class StrictMode { return enable(DETECT_VM_UNTAGGED_SOCKET); } + /** @hide */ + public Builder permitUntaggedSockets() { + return disable(DETECT_VM_UNTAGGED_SOCKET); + } + /** * Crashes the whole process on violation. This penalty runs at the end of all enabled * penalties so you'll still get your logging or other violations before the process @@ -953,7 +972,8 @@ public final class StrictMode { setThreadPolicyMask(policy.mask); } - private static void setThreadPolicyMask(final int policyMask) { + /** @hide */ + public static void setThreadPolicyMask(final int policyMask) { // In addition to the Java-level thread-local in Dalvik's // BlockGuard, we also need to keep a native thread-local in // Binder in order to propagate the value across Binder calls, @@ -1019,12 +1039,17 @@ public final class StrictMode { * end of a block */ public static ThreadPolicy allowThreadDiskWrites() { + return new ThreadPolicy(allowThreadDiskWritesMask()); + } + + /** @hide */ + public static int allowThreadDiskWritesMask() { int oldPolicyMask = getThreadPolicyMask(); int newPolicyMask = oldPolicyMask & ~(DETECT_DISK_WRITE | DETECT_DISK_READ); if (newPolicyMask != oldPolicyMask) { setThreadPolicyMask(newPolicyMask); } - return new ThreadPolicy(oldPolicyMask); + return oldPolicyMask; } /** @@ -1035,31 +1060,52 @@ public final class StrictMode { * @return the old policy, to be passed to setThreadPolicy to restore the policy. */ public static ThreadPolicy allowThreadDiskReads() { + return new ThreadPolicy(allowThreadDiskReadsMask()); + } + + /** @hide */ + public static int allowThreadDiskReadsMask() { int oldPolicyMask = getThreadPolicyMask(); int newPolicyMask = oldPolicyMask & ~(DETECT_DISK_READ); if (newPolicyMask != oldPolicyMask) { setThreadPolicyMask(newPolicyMask); } - return new ThreadPolicy(oldPolicyMask); + return oldPolicyMask; } - // We don't want to flash the screen red in the system server - // process, nor do we want to modify all the call sites of - // conditionallyEnableDebugLogging() in the system server, - // so instead we use this to determine if we are the system server. - private static boolean amTheSystemServerProcess() { - // Fast path. Most apps don't have the system server's UID. - if (Process.myUid() != Process.SYSTEM_UID) { - return false; - } + /** + * Determine if the given app is "bundled" as part of the system image. + * These bundled apps are developed in lock-step with the OS, and they + * aren't updated outside of an OTA, so we want to chase any + * {@link StrictMode} regressions by enabling detection when running on + * {@link Build#IS_USERDEBUG} or {@link Build#IS_ENG} builds. + *
+ * Unbundled apps included in the system image are expected to detect and + * triage their own {@link StrictMode} issues separate from the OS release + * process, which is why we don't enable them here. + * + * @hide + */ + public static boolean isBundledSystemApp(ApplicationInfo ai) { + if (ai == null || ai.packageName == null) { + // Probably system server + return true; + } else if (ai.isSystemApp()) { + // Ignore unbundled apps living in the wrong namespace + if (ai.packageName.equals("com.android.vending") + || ai.packageName.equals("com.android.chrome")) { + return false; + } - // The settings app, though, has the system server's UID so - // look up our stack to see if we came from the system server. - Throwable stack = new Throwable(); - stack.fillInStackTrace(); - for (StackTraceElement ste : stack.getStackTrace()) { - String clsName = ste.getClassName(); - if (clsName != null && clsName.startsWith("com.android.server.")) { + // Ignore bundled apps that are way too spammy + // STOPSHIP: burn this list down to zero + if (ai.packageName.equals("com.android.phone")) { + return false; + } + + if (ai.packageName.equals("android") + || ai.packageName.startsWith("android.") + || ai.packageName.startsWith("com.android.")) { return true; } } @@ -1067,81 +1113,81 @@ public final class StrictMode { } /** - * Enable DropBox logging for debug phone builds. + * Initialize default {@link ThreadPolicy} for the current thread. * * @hide */ - public static boolean conditionallyEnableDebugLogging() { - boolean doFlashes = - SystemProperties.getBoolean(VISUAL_PROPERTY, false) && !amTheSystemServerProcess(); - final boolean suppress = SystemProperties.getBoolean(DISABLE_PROPERTY, false); + public static void initThreadDefaults(ApplicationInfo ai) { + final ThreadPolicy.Builder builder = new ThreadPolicy.Builder(); + final int targetSdkVersion = (ai != null) ? ai.targetSdkVersion + : Build.VERSION_CODES.CUR_DEVELOPMENT; - // For debug builds, log event loop stalls to dropbox for analysis. - // Similar logic also appears in ActivityThread.java for system apps. - if (!doFlashes && (Build.IS_USER || suppress)) { - setCloseGuardEnabled(false); - return false; + // Starting in HC, we don't allow network usage on the main thread + if (targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) { + builder.detectNetwork(); + builder.penaltyDeathOnNetwork(); } - // Eng builds have flashes on all the time. The suppression property - // overrides this, so we force the behavior only after the short-circuit - // check above. - if (Build.IS_ENG) { - doFlashes = true; - } - - // Thread policy controls BlockGuard. - int threadPolicyMask = - StrictMode.DETECT_DISK_WRITE - | StrictMode.DETECT_DISK_READ - | StrictMode.DETECT_NETWORK; - - if (!Build.IS_USER) { - threadPolicyMask |= StrictMode.PENALTY_DROPBOX; - } - if (doFlashes) { - threadPolicyMask |= StrictMode.PENALTY_FLASH; - } - - StrictMode.setThreadPolicyMask(threadPolicyMask); - - // VM Policy controls CloseGuard, detection of Activity leaks, - // and instance counting. - if (Build.IS_USER) { - setCloseGuardEnabled(false); - } else { - VmPolicy.Builder policyBuilder = new VmPolicy.Builder().detectAll(); - if (!Build.IS_ENG) { - // Activity leak detection causes too much slowdown for userdebug because of the - // GCs. - policyBuilder = policyBuilder.disable(DETECT_VM_ACTIVITY_LEAKS); + if (Build.IS_USER || DISABLE || SystemProperties.getBoolean(DISABLE_PROPERTY, false)) { + // Detect nothing extra + } else if (Build.IS_USERDEBUG) { + // Detect everything in bundled apps + if (isBundledSystemApp(ai)) { + builder.detectAll(); + builder.penaltyDropBox(); + if (SystemProperties.getBoolean(VISUAL_PROPERTY, false)) { + builder.penaltyFlashScreen(); + } } - policyBuilder = policyBuilder.penaltyDropBox(); - if (Build.IS_ENG) { - policyBuilder.penaltyLog(); + } else if (Build.IS_ENG) { + // Detect everything in bundled apps + if (isBundledSystemApp(ai)) { + builder.detectAll(); + builder.penaltyDropBox(); + builder.penaltyLog(); + builder.penaltyFlashScreen(); } - // All core system components need to tag their sockets to aid - // system health investigations - if (android.os.Process.myUid() < android.os.Process.FIRST_APPLICATION_UID) { - policyBuilder.enable(DETECT_VM_UNTAGGED_SOCKET); - } else { - policyBuilder.disable(DETECT_VM_UNTAGGED_SOCKET); - } - setVmPolicy(policyBuilder.build()); - setCloseGuardEnabled(vmClosableObjectLeaksEnabled()); } - return true; + + setThreadPolicy(builder.build()); } /** - * Used by the framework to make network usage on the main thread a fatal error. + * Initialize default {@link VmPolicy} for the current VM. * * @hide */ - public static void enableDeathOnNetwork() { - int oldPolicy = getThreadPolicyMask(); - int newPolicy = oldPolicy | DETECT_NETWORK | PENALTY_DEATH_ON_NETWORK; - setThreadPolicyMask(newPolicy); + public static void initVmDefaults(ApplicationInfo ai) { + final VmPolicy.Builder builder = new VmPolicy.Builder(); + final int targetSdkVersion = (ai != null) ? ai.targetSdkVersion + : Build.VERSION_CODES.CUR_DEVELOPMENT; + + // Starting in N, we don't allow file:// Uri exposure + if (targetSdkVersion >= Build.VERSION_CODES.N) { + builder.detectFileUriExposure(); + builder.penaltyDeathOnFileUriExposure(); + } + + if (Build.IS_USER || DISABLE || SystemProperties.getBoolean(DISABLE_PROPERTY, false)) { + // Detect nothing extra + } else if (Build.IS_USERDEBUG) { + // Detect everything in bundled apps (except activity leaks, which + // are expensive to track) + if (isBundledSystemApp(ai)) { + builder.detectAll(); + builder.permitActivityLeaks(); + builder.penaltyDropBox(); + } + } else if (Build.IS_ENG) { + // Detect everything in bundled apps + if (isBundledSystemApp(ai)) { + builder.detectAll(); + builder.penaltyDropBox(); + builder.penaltyLog(); + } + } + + setVmPolicy(builder.build()); } /** diff --git a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java index 8884d24ff0acf..a39997d3dce18 100644 --- a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java +++ b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java @@ -20,6 +20,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.StrictMode; import android.os.SystemClock; import android.util.IntArray; import android.util.Slog; @@ -82,6 +83,7 @@ public class KernelUidCpuFreqTimeReader { if (!mProcFileAvailable && mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) { return null; } + final int oldMask = StrictMode.allowThreadDiskReadsMask(); try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) { mProcFileAvailable = true; return readFreqs(reader, powerProfile); @@ -89,6 +91,8 @@ public class KernelUidCpuFreqTimeReader { mReadErrorCounter++; Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e); return null; + } finally { + StrictMode.setThreadPolicyMask(oldMask); } } @@ -106,12 +110,15 @@ public class KernelUidCpuFreqTimeReader { if (!mProcFileAvailable) { return; } + final int oldMask = StrictMode.allowThreadDiskReadsMask(); try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) { mNowTimeMs = SystemClock.elapsedRealtime(); readDelta(reader, callback); mLastTimeReadMs = mNowTimeMs; } catch (IOException e) { Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e); + } finally { + StrictMode.setThreadPolicyMask(oldMask); } } diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java index 37d9d1d475ee3..65615c0ffb02d 100644 --- a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java +++ b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java @@ -16,6 +16,7 @@ package com.android.internal.os; import android.annotation.Nullable; +import android.os.StrictMode; import android.os.SystemClock; import android.text.TextUtils; import android.util.Slog; @@ -65,6 +66,7 @@ public class KernelUidCpuTimeReader { * a fresh delta. */ public void readDelta(@Nullable Callback callback) { + final int oldMask = StrictMode.allowThreadDiskReadsMask(); long nowUs = SystemClock.elapsedRealtime() * 1000; try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) { TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' '); @@ -121,6 +123,8 @@ public class KernelUidCpuTimeReader { } } catch (IOException e) { Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage()); + } finally { + StrictMode.setThreadPolicyMask(oldMask); } mLastTimeReadUs = nowUs; } @@ -160,12 +164,15 @@ public class KernelUidCpuTimeReader { private void removeUidsFromKernelModule(int startUid, int endUid) { Slog.d(TAG, "Removing uids " + startUid + "-" + endUid); + final int oldMask = StrictMode.allowThreadDiskWritesMask(); try (FileWriter writer = new FileWriter(sRemoveUidProcFile)) { writer.write(startUid + "-" + endUid); writer.flush(); } catch (IOException e) { Slog.e(TAG, "failed to remove uids " + startUid + " - " + endUid + " from uid_cputime module", e); + } finally { + StrictMode.setThreadPolicyMask(oldMask); } } } diff --git a/services/core/java/com/android/server/ServiceThread.java b/services/core/java/com/android/server/ServiceThread.java index bce64afd0c5e4..26703c514a962 100644 --- a/services/core/java/com/android/server/ServiceThread.java +++ b/services/core/java/com/android/server/ServiceThread.java @@ -19,7 +19,6 @@ package com.android.server; import android.os.HandlerThread; import android.os.Process; import android.os.StrictMode; -import android.util.Slog; /** * Special handler thread that we create for system services that require their own loopers. @@ -38,11 +37,10 @@ public class ServiceThread extends HandlerThread { public void run() { Process.setCanSelfBackground(false); - // For debug builds, log event loop stalls to dropbox for analysis. - if (!mAllowIo && StrictMode.conditionallyEnableDebugLogging()) { - Slog.i(TAG, "Enabled StrictMode logging for " + getName() + " looper."); + if (!mAllowIo) { + StrictMode.initThreadDefaults(null); } super.run(); } -} \ No newline at end of file +} diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 803691e939cbc..fc4cb1a05dc1a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -14706,7 +14706,12 @@ public class ActivityManagerService extends IActivityManager.Stub if (process == null) { // If process is null, we are being called from some internal code // and may be about to die -- run this synchronously. - worker.run(); + final int oldMask = StrictMode.allowThreadDiskWritesMask(); + try { + worker.run(); + } finally { + StrictMode.setThreadPolicyMask(oldMask); + } } else { worker.start(); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 6520dc94072ff..fd186ce68a90a 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -183,6 +183,7 @@ import android.os.PowerManagerInternal; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.StrictMode; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UEventObserver; @@ -5798,6 +5799,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { } void initializeHdmiState() { + final int oldMask = StrictMode.allowThreadDiskReadsMask(); + try { + initializeHdmiStateInternal(); + } finally { + StrictMode.setThreadPolicyMask(oldMask); + } + } + + void initializeHdmiStateInternal() { boolean plugged = false; // watch for HDMI plug messages if the hdmi switch exists if (new File("/sys/devices/virtual/switch/hdmi/state").exists()) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index bbbfeaa443ec7..f8bcb73a8f83a 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -406,10 +406,8 @@ public final class SystemServer { traceEnd(); } - // For debug builds, log event loop stalls to dropbox for analysis. - if (StrictMode.conditionallyEnableDebugLogging()) { - Slog.i(TAG, "Enabled StrictMode for system server main thread."); - } + StrictMode.initVmDefaults(null); + if (!mRuntimeRestart && !isFirstBootOrUpgrade()) { int uptimeMillis = (int) SystemClock.elapsedRealtime(); MetricsLogger.histogram(null, "boot_system_server_ready", uptimeMillis);