Refactor DialerStrictMode into an interface.

-bypassed violations are no longer logged in AospDialer
The default implementation will use system strict mode and crash on bugfood build same as before.

Bug: 66003745
Test: manual
PiperOrigin-RevId: 170214128
Change-Id: Iab630f19499e90b15eb0b7f0707b4a70c7d81fbe
diff --git a/java/com/android/contacts/common/preference/ContactsPreferences.java b/java/com/android/contacts/common/preference/ContactsPreferences.java
index 399700e..d0adb04 100644
--- a/java/com/android/contacts/common/preference/ContactsPreferences.java
+++ b/java/com/android/contacts/common/preference/ContactsPreferences.java
@@ -27,7 +27,7 @@
 import android.text.TextUtils;
 import com.android.contacts.common.R;
 import com.android.contacts.common.model.account.AccountWithDataSet;
-import com.android.dialer.strictmode.DialerStrictMode;
+import com.android.dialer.strictmode.StrictModeUtils;
 
 /** Manages user preferences for contacts. */
 public class ContactsPreferences implements OnSharedPreferenceChangeListener {
@@ -107,7 +107,7 @@
     mSortOrder = sortOrder;
     final Editor editor = mPreferences.edit();
     editor.putInt(SORT_ORDER_KEY, sortOrder);
-    DialerStrictMode.bypass(editor::commit);
+    StrictModeUtils.bypass(editor::commit);
   }
 
   private boolean isDisplayOrderUserChangeable() {
@@ -136,7 +136,7 @@
     mDisplayOrder = displayOrder;
     final Editor editor = mPreferences.edit();
     editor.putInt(DISPLAY_ORDER_KEY, displayOrder);
-    DialerStrictMode.bypass(editor::commit);
+    StrictModeUtils.bypass(editor::commit);
   }
 
   private boolean isDefaultAccountUserChangeable() {
@@ -166,7 +166,7 @@
       editor.putString(mDefaultAccountKey, accountWithDataSet.stringify());
     }
     editor.putBoolean(mDefaultAccountSavedKey, true);
-    DialerStrictMode.bypass(editor::commit);
+    StrictModeUtils.bypass(editor::commit);
   }
 
   public void registerChangeListener(ChangeListener listener) {
diff --git a/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java b/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java
index 50db7f7..d61f712 100644
--- a/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java
+++ b/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java
@@ -26,6 +26,7 @@
 import com.android.dialer.phonenumbergeoutil.impl.PhoneNumberGeoUtilModule;
 import com.android.dialer.simulator.impl.SimulatorModule;
 import com.android.dialer.storage.StorageModule;
+import com.android.dialer.strictmode.impl.SystemStrictModeModule;
 import com.android.incallui.calllocation.stub.StubCallLocationModule;
 import com.android.incallui.maps.stub.StubMapsModule;
 import com.android.voicemail.impl.VoicemailModule;
@@ -43,6 +44,7 @@
     SharedPrefConfigProviderModule.class,
     SimulatorModule.class,
     StorageModule.class,
+    SystemStrictModeModule.class,
     StubCallLocationModule.class,
     StubEnrichedCallModule.class,
     StubMapsModule.class,
diff --git a/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java b/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java
index 30b1e8a..580eb5d 100644
--- a/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java
+++ b/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java
@@ -26,6 +26,7 @@
 import com.android.dialer.phonenumbergeoutil.PhoneNumberGeoUtilComponent;
 import com.android.dialer.simulator.SimulatorComponent;
 import com.android.dialer.storage.StorageComponent;
+import com.android.dialer.strictmode.StrictModeComponent;
 import com.android.incallui.calllocation.CallLocationComponent;
 import com.android.incallui.maps.MapsComponent;
 import com.android.voicemail.VoicemailComponent;
@@ -46,5 +47,6 @@
         PhoneNumberGeoUtilComponent.HasComponent,
         SimulatorComponent.HasComponent,
         StorageComponent.HasComponent,
+        StrictModeComponent.HasComponent,
         VoicemailComponent.HasComponent,
         LightbringerComponent.HasComponent {}
diff --git a/java/com/android/dialer/binary/common/DialerApplication.java b/java/com/android/dialer/binary/common/DialerApplication.java
index 580e0a3..19af575 100644
--- a/java/com/android/dialer/binary/common/DialerApplication.java
+++ b/java/com/android/dialer/binary/common/DialerApplication.java
@@ -27,7 +27,7 @@
 import com.android.dialer.inject.HasRootComponent;
 import com.android.dialer.notification.NotificationChannelManager;
 import com.android.dialer.persistentlog.PersistentLogger;
-import com.android.dialer.strictmode.DialerStrictMode;
+import com.android.dialer.strictmode.StrictModeComponent;
 
 /** A common application subclass for all Dialer build variants. */
 public abstract class DialerApplication extends Application implements HasRootComponent {
@@ -37,7 +37,7 @@
   @Override
   public void onCreate() {
     Trace.beginSection("DialerApplication.onCreate");
-    DialerStrictMode.onApplicationCreate(this);
+    StrictModeComponent.get(this).getDialerStrictMode().onApplicationCreate(this);
 
     super.onCreate();
     new BlockedNumbersAutoMigrator(
diff --git a/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java b/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java
index cf51377..87f09c8 100644
--- a/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java
+++ b/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java
@@ -26,6 +26,7 @@
 import com.android.dialer.phonenumbergeoutil.impl.PhoneNumberGeoUtilModule;
 import com.android.dialer.simulator.impl.SimulatorModule;
 import com.android.dialer.storage.StorageModule;
+import com.android.dialer.strictmode.impl.SystemStrictModeModule;
 import com.android.incallui.calllocation.impl.CallLocationModule;
 import com.android.incallui.maps.impl.MapsModule;
 import com.android.voicemail.impl.VoicemailModule;
@@ -47,6 +48,7 @@
     SharedPrefConfigProviderModule.class,
     SimulatorModule.class,
     StorageModule.class,
+    SystemStrictModeModule.class,
     StubEnrichedCallModule.class,
     MapsModule.class,
     VoicemailModule.class,
diff --git a/java/com/android/dialer/blocking/FilteredNumberCompat.java b/java/com/android/dialer/blocking/FilteredNumberCompat.java
index 548c965..a5f3d7e 100644
--- a/java/com/android/dialer/blocking/FilteredNumberCompat.java
+++ b/java/com/android/dialer/blocking/FilteredNumberCompat.java
@@ -38,7 +38,7 @@
 import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
 import com.android.dialer.database.FilteredNumberContract.FilteredNumberSources;
 import com.android.dialer.database.FilteredNumberContract.FilteredNumberTypes;
-import com.android.dialer.strictmode.DialerStrictMode;
+import com.android.dialer.strictmode.StrictModeUtils;
 import com.android.dialer.telecom.TelecomUtil;
 import java.util.ArrayList;
 import java.util.List;
@@ -125,7 +125,7 @@
    *     android.provider.BlockedNumberContract} blocking, {@code false} otherwise.
    */
   public static boolean hasMigratedToNewBlocking(Context context) {
-    return DialerStrictMode.bypass(
+    return StrictModeUtils.bypass(
         () ->
             PreferenceManager.getDefaultSharedPreferences(context)
                 .getBoolean(HAS_MIGRATED_TO_NEW_BLOCKING_KEY, false));
diff --git a/java/com/android/dialer/configprovider/SharedPrefConfigProvider.java b/java/com/android/dialer/configprovider/SharedPrefConfigProvider.java
index 6ee4695..5351698 100644
--- a/java/com/android/dialer/configprovider/SharedPrefConfigProvider.java
+++ b/java/com/android/dialer/configprovider/SharedPrefConfigProvider.java
@@ -24,7 +24,7 @@
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.inject.ApplicationContext;
-import com.android.dialer.strictmode.DialerStrictMode;
+import com.android.dialer.strictmode.StrictModeUtils;
 import com.android.dialer.util.DialerUtils;
 import javax.inject.Inject;
 
@@ -96,21 +96,21 @@
   @Override
   public String getString(String key, String defaultValue) {
     // Reading shared prefs on the main thread is generally safe since a single instance is cached.
-    return DialerStrictMode.bypass(
+    return StrictModeUtils.bypass(
         () -> getSharedPrefs(appContext).getString(PREF_PREFIX + key, defaultValue));
   }
 
   @Override
   public long getLong(String key, long defaultValue) {
     // Reading shared prefs on the main thread is generally safe since a single instance is cached.
-    return DialerStrictMode.bypass(
+    return StrictModeUtils.bypass(
         () -> getSharedPrefs(appContext).getLong(PREF_PREFIX + key, defaultValue));
   }
 
   @Override
   public boolean getBoolean(String key, boolean defaultValue) {
     // Reading shared prefs on the main thread is generally safe since a single instance is cached.
-    return DialerStrictMode.bypass(
+    return StrictModeUtils.bypass(
         () -> getSharedPrefs(appContext).getBoolean(PREF_PREFIX + key, defaultValue));
   }
 
diff --git a/java/com/android/dialer/persistentlog/PersistentLogger.java b/java/com/android/dialer/persistentlog/PersistentLogger.java
index 5fdefd1..608602e 100644
--- a/java/com/android/dialer/persistentlog/PersistentLogger.java
+++ b/java/com/android/dialer/persistentlog/PersistentLogger.java
@@ -26,7 +26,7 @@
 import android.support.v4.os.UserManagerCompat;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
-import com.android.dialer.strictmode.DialerStrictMode;
+import com.android.dialer.strictmode.StrictModeUtils;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
@@ -178,7 +178,7 @@
   }
 
   private static byte[] buildTextLog(String tag, String string) {
-    Calendar c = DialerStrictMode.bypass(() -> Calendar.getInstance());
+    Calendar c = StrictModeUtils.bypass(() -> Calendar.getInstance());
     return String.format("%tm-%td %tH:%tM:%tS.%tL - %s - %s", c, c, c, c, c, c, tag, string)
         .getBytes(StandardCharsets.UTF_8);
   }
diff --git a/java/com/android/dialer/strictmode/DialerStrictMode.java b/java/com/android/dialer/strictmode/DialerStrictMode.java
index f895f7c..462db57 100644
--- a/java/com/android/dialer/strictmode/DialerStrictMode.java
+++ b/java/com/android/dialer/strictmode/DialerStrictMode.java
@@ -17,131 +17,12 @@
 package com.android.dialer.strictmode;
 
 import android.app.Application;
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.StrictMode;
-import android.os.StrictMode.ThreadPolicy;
-import android.os.StrictMode.VmPolicy;
-import android.preference.PreferenceManager;
-import android.support.annotation.AnyThread;
 import android.support.annotation.MainThread;
-import android.support.v4.os.UserManagerCompat;
-import com.android.dialer.buildtype.BuildType;
-import com.android.dialer.function.Supplier;
-import com.android.dialer.util.DialerUtils;
 
-/** Enables strict mode for the application, and provides means of temporarily disabling it. */
-public final class DialerStrictMode {
-
-  private static final VmPolicy VM_DEATH_PENALTY =
-      new StrictMode.VmPolicy.Builder().penaltyLog().penaltyDeath().build();
-
-  private static final ThreadPolicy THREAD_LOG_PENALTY =
-      new StrictMode.ThreadPolicy.Builder().penaltyLog().build();
-  private static final ThreadPolicy THREAD_DEATH_PENALTY =
-      new StrictMode.ThreadPolicy.Builder().penaltyLog().penaltyDeath().build();
-
-  private DialerStrictMode() {}
+/** Interface for strict mode to handle strict mode violations. */
+public interface DialerStrictMode {
 
   /** Initializes strict mode on application start. */
   @MainThread
-  public static void onApplicationCreate(Application application) {
-    if (isStrictModeAllowed()) {
-      warmupSharedPrefs(application);
-      StrictModeUtils.setRecommendedMainThreadPolicy(THREAD_DEATH_PENALTY);
-      StrictModeUtils.setRecommendedVMPolicy(VM_DEATH_PENALTY);
-
-      // Because Android resets StrictMode policies after Application.onCreate is done, we set it
-      // again right after.
-      // See cl/105932355 for the discussion.
-      // See b/36951662 for the public bug.
-      Handler handler = new Handler(Looper.myLooper());
-      handler.postAtFrontOfQueue(
-          () -> StrictModeUtils.setRecommendedMainThreadPolicy(THREAD_DEATH_PENALTY));
-    }
-  }
-
-  /**
-   * We frequently access shared preferences on the main thread, which causes strict mode
-   * violations. When strict mode is allowed, warm up the shared preferences so that later uses of
-   * shared preferences access the in-memory versions and we don't have to bypass strict mode at
-   * every point in the application where shared preferences are accessed.
-   */
-  private static void warmupSharedPrefs(Application application) {
-    // From credential-encrypted (CE) storage, i.e.:
-    //    /data/data/com.android.dialer/shared_prefs
-
-    if (UserManagerCompat.isUserUnlocked(application)) {
-      // <package_name>_preferences.xml
-      PreferenceManager.getDefaultSharedPreferences(application);
-
-      // <package_name>.xml
-      application.getSharedPreferences(application.getPackageName(), Context.MODE_PRIVATE);
-    }
-
-    // From device-encrypted (DE) storage, i.e.:
-    //   /data/user_de/0/com.android.dialer/shared_prefs/
-
-    // <package_name>_preferences.xml
-    DialerUtils.getDefaultSharedPreferenceForDeviceProtectedStorageContext(application);
-  }
-
-  private static boolean isStrictModeAllowed() {
-    return BuildType.get() == BuildType.BUGFOOD;
-  }
-
-  private static boolean onMainThread() {
-    return Looper.getMainLooper().equals(Looper.myLooper());
-  }
-
-  /**
-   * Convenience method for disabling and enabling the thread policy death penalty using lambdas.
-   *
-   * <p>For example:
-   *
-   * <p><code>
-   *   Value foo = DialerStrictMode.bypass(() -> doDiskAccessOnMainThreadReturningValue());
-   * </code>
-   *
-   * <p>The thread policy is only mutated if this is called from the main thread.
-   */
-  @AnyThread
-  public static <T> T bypass(Supplier<T> supplier) {
-    if (isStrictModeAllowed() && onMainThread()) {
-      ThreadPolicy originalPolicy = StrictMode.getThreadPolicy();
-      StrictModeUtils.setRecommendedMainThreadPolicy(THREAD_LOG_PENALTY);
-      try {
-        return supplier.get();
-      } finally {
-        StrictMode.setThreadPolicy(originalPolicy);
-      }
-    }
-    return supplier.get();
-  }
-
-  /**
-   * Convenience method for disabling and enabling the thread policy death penalty using lambdas.
-   *
-   * <p>For example:
-   *
-   * <p><code>
-   *   DialerStrictMode.bypass(() -> doDiskAccessOnMainThread());
-   * </code>
-   *
-   * <p>The thread policy is only mutated if this is called from the main thread.
-   */
-  @AnyThread
-  public static void bypass(Runnable runnable) {
-    if (isStrictModeAllowed() && onMainThread()) {
-      ThreadPolicy originalPolicy = StrictMode.getThreadPolicy();
-      StrictModeUtils.setRecommendedMainThreadPolicy(THREAD_LOG_PENALTY);
-      try {
-        runnable.run();
-      } finally {
-        StrictMode.setThreadPolicy(originalPolicy);
-      }
-    }
-    runnable.run();
-  }
+  void onApplicationCreate(Application application);
 }
diff --git a/java/com/android/dialer/strictmode/StrictModeComponent.java b/java/com/android/dialer/strictmode/StrictModeComponent.java
new file mode 100644
index 0000000..7b9f48e
--- /dev/null
+++ b/java/com/android/dialer/strictmode/StrictModeComponent.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.dialer.strictmode;
+
+import android.content.Context;
+import com.android.dialer.inject.HasRootComponent;
+import dagger.Subcomponent;
+
+/** Dagger component for DialerStrictMode. */
+@Subcomponent
+public abstract class StrictModeComponent {
+
+  public abstract DialerStrictMode getDialerStrictMode();
+
+  public static StrictModeComponent get(Context context) {
+    return ((StrictModeComponent.HasComponent)
+            ((HasRootComponent) context.getApplicationContext()).component())
+        .strictModeComponent();
+  }
+
+  /** Used to refer to the root application component. */
+  public interface HasComponent {
+    StrictModeComponent strictModeComponent();
+  }
+}
diff --git a/java/com/android/dialer/strictmode/StrictModeUtils.java b/java/com/android/dialer/strictmode/StrictModeUtils.java
index 6944fd4..c83beb0 100644
--- a/java/com/android/dialer/strictmode/StrictModeUtils.java
+++ b/java/com/android/dialer/strictmode/StrictModeUtils.java
@@ -16,104 +16,76 @@
 
 package com.android.dialer.strictmode;
 
-import android.os.Build;
+import android.os.Looper;
 import android.os.StrictMode;
-import android.support.annotation.Nullable;
-import com.android.dialer.common.Assert;
-import com.google.auto.value.AutoValue;
-import java.util.Map;
-import java.util.Map.Entry;
+import android.os.StrictMode.ThreadPolicy;
+import android.support.annotation.AnyThread;
+import com.android.dialer.buildtype.BuildType;
+import com.android.dialer.function.Supplier;
 
 /** Utilities for enforcing strict-mode in an app. */
-final class StrictModeUtils {
+public final class StrictModeUtils {
+
+  private static final ThreadPolicy THREAD_NO_PENALTY =
+      new StrictMode.ThreadPolicy.Builder().permitAll().build();
 
   /**
-   * Set the recommended policy for the app.
+   * Convenience method for disabling and enabling the thread policy death penalty using lambdas.
    *
-   * @param threadPenalties policy with preferred penalties. Detection bits will be ignored.
-   */
-  static void setRecommendedMainThreadPolicy(StrictMode.ThreadPolicy threadPenalties) {
-    StrictMode.ThreadPolicy threadPolicy =
-        new StrictMode.ThreadPolicy.Builder(threadPenalties).detectAll().build();
-    StrictMode.setThreadPolicy(threadPolicy);
-  }
-
-  /**
-   * Set the recommended policy for the app.
+   * <p>For example:
    *
-   * @param vmPenalties policy with preferred penalties. Detection bits should be unset.
-   */
-  static void setRecommendedVMPolicy(StrictMode.VmPolicy vmPenalties) {
-    setRecommendedVMPolicy(vmPenalties, StrictModeVmConfig.empty());
-  }
-
-  /**
-   * Set the recommended policy for the app.
+   * <p><code>
+   *   Value foo = StrictModeUtils.bypass(() -> doDiskAccessOnMainThreadReturningValue());
+   * </code>
    *
-   * @param vmPenalties policy with preferred penalties. Detection bits should be unset.
+   * <p>The thread policy is only mutated if this is called from the main thread.
    */
-  private static void setRecommendedVMPolicy(
-      StrictMode.VmPolicy vmPenalties, StrictModeVmConfig config) {
-    Assert.isNotNull(config);
-    StrictMode.VmPolicy.Builder vmPolicyBuilder =
-        new StrictMode.VmPolicy.Builder(vmPenalties)
-            .detectLeakedClosableObjects()
-            .detectLeakedSqlLiteObjects();
-    if (Build.VERSION.SDK_INT >= 16) {
-      vmPolicyBuilder.detectLeakedRegistrationObjects();
-    }
-    if (Build.VERSION.SDK_INT >= 18) {
-      vmPolicyBuilder.detectFileUriExposure();
-    }
-    if (Build.VERSION.SDK_INT >= 21) {
-      // Even though this API is available earlier, it did not properly run finalizers.
-      // This avoids lots of false positives.
-
-      // TODO(zachh): Use LeakCanary and remove this line.
-      vmPolicyBuilder.detectActivityLeaks();
-
-      if (config.maxInstanceLimits() != null) {
-        for (Entry<Class<?>, Integer> entry : config.maxInstanceLimits().entrySet()) {
-          vmPolicyBuilder.setClassInstanceLimit(entry.getKey(), entry.getValue());
-        }
+  @AnyThread
+  public static <T> T bypass(Supplier<T> supplier) {
+    if (isStrictModeAllowed() && onMainThread()) {
+      ThreadPolicy originalPolicy = StrictMode.getThreadPolicy();
+      StrictMode.setThreadPolicy(THREAD_NO_PENALTY);
+      try {
+        return supplier.get();
+      } finally {
+        StrictMode.setThreadPolicy(originalPolicy);
       }
     }
-    if (Build.VERSION.SDK_INT >= 23) {
-      // TODO(azlatin): Enable clear-text check once b/36730713 is fixed.
-      // vmPolicyBuilder.detectCleartextNetwork();
-    }
-    // Once OC Lands:
-    // .detectContentUriWithoutPermission()
-    // .detectUntaggedSockets()
-    StrictMode.setVmPolicy(vmPolicyBuilder.build());
+    return supplier.get();
   }
 
-  /** VmPolicy configuration. */
-  @AutoValue
-  abstract static class StrictModeVmConfig {
-    /** A map of a class to the maximum number of allowed instances of that class. */
-    @Nullable
-    abstract Map<Class<?>, Integer> maxInstanceLimits();
-
-    public static StrictModeVmConfig empty() {
-      return builder().build();
+  /**
+   * Convenience method for disabling and enabling the thread policy death penalty using lambdas.
+   *
+   * <p>For example:
+   *
+   * <p><code>
+   *   StrictModeUtils.bypass(() -> doDiskAccessOnMainThread());
+   * </code>
+   *
+   * <p>The thread policy is only mutated if this is called from the main thread.
+   */
+  @AnyThread
+  public static void bypass(Runnable runnable) {
+    if (isStrictModeAllowed() && onMainThread()) {
+      ThreadPolicy originalPolicy = StrictMode.getThreadPolicy();
+      StrictMode.setThreadPolicy(THREAD_NO_PENALTY);
+      try {
+        runnable.run();
+      } finally {
+        StrictMode.setThreadPolicy(originalPolicy);
+      }
+    } else {
+      runnable.run();
     }
+  }
 
-    public static Builder builder() {
-      return new AutoValue_StrictModeUtils_StrictModeVmConfig.Builder();
-    }
+  public static boolean isStrictModeAllowed() {
+    return BuildType.get() == BuildType.BUGFOOD;
+  }
 
-    /** VmPolicy configuration builder. */
-    @AutoValue.Builder
-    public abstract static class Builder {
-      public abstract Builder setMaxInstanceLimits(Map<Class<?>, Integer> limits);
-
-      public abstract StrictModeVmConfig build();
-
-      Builder() {}
-    }
-
-    StrictModeVmConfig() {}
+  private static boolean onMainThread() {
+    return Looper.getMainLooper().equals(Looper.myLooper());
   }
 
   private StrictModeUtils() {}
diff --git a/java/com/android/dialer/strictmode/impl/SystemDialerStrictMode.java b/java/com/android/dialer/strictmode/impl/SystemDialerStrictMode.java
new file mode 100644
index 0000000..4d65241
--- /dev/null
+++ b/java/com/android/dialer/strictmode/impl/SystemDialerStrictMode.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.dialer.strictmode.impl;
+
+import android.app.Application;
+import android.content.Context;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.StrictMode;
+import android.os.StrictMode.ThreadPolicy;
+import android.os.StrictMode.VmPolicy;
+import android.preference.PreferenceManager;
+import android.support.annotation.MainThread;
+import android.support.annotation.Nullable;
+import android.support.v4.os.UserManagerCompat;
+import com.android.dialer.buildtype.BuildType;
+import com.android.dialer.common.Assert;
+import com.android.dialer.strictmode.DialerStrictMode;
+import com.android.dialer.util.DialerUtils;
+import com.google.auto.value.AutoValue;
+import java.util.Map;
+import javax.inject.Inject;
+
+final class SystemDialerStrictMode implements DialerStrictMode {
+  private static final VmPolicy VM_DEATH_PENALTY =
+      new StrictMode.VmPolicy.Builder().penaltyLog().penaltyDeath().build();
+
+  private static final ThreadPolicy THREAD_DEATH_PENALTY =
+      new StrictMode.ThreadPolicy.Builder().penaltyLog().penaltyDeath().build();
+
+  @Inject
+  public SystemDialerStrictMode() {}
+
+  @MainThread
+  @Override
+  public void onApplicationCreate(Application application) {
+    if (isStrictModeAllowed()) {
+      warmupSharedPrefs(application);
+      setRecommendedMainThreadPolicy(THREAD_DEATH_PENALTY);
+      setRecommendedVMPolicy(VM_DEATH_PENALTY);
+
+      // Because Android resets StrictMode policies after Application.onCreate is done, we set it
+      // again right after.
+      // See cl/105932355 for the discussion.
+      // See b/36951662 for the public bug.
+      Handler handler = new Handler(Looper.myLooper());
+      handler.postAtFrontOfQueue(() -> setRecommendedMainThreadPolicy(THREAD_DEATH_PENALTY));
+    }
+  }
+
+  /**
+   * We frequently access shared preferences on the main thread, which causes strict mode
+   * violations. When strict mode is allowed, warm up the shared preferences so that later uses of
+   * shared preferences access the in-memory versions and we don't have to bypass strict mode at
+   * every point in the application where shared preferences are accessed.
+   */
+  private static void warmupSharedPrefs(Application application) {
+    // From credential-encrypted (CE) storage, i.e.:
+    //    /data/data/com.android.dialer/shared_prefs
+
+    if (UserManagerCompat.isUserUnlocked(application)) {
+      // <package_name>_preferences.xml
+      PreferenceManager.getDefaultSharedPreferences(application);
+
+      // <package_name>.xml
+      application.getSharedPreferences(application.getPackageName(), Context.MODE_PRIVATE);
+    }
+
+    // From device-encrypted (DE) storage, i.e.:
+    //   /data/user_de/0/com.android.dialer/shared_prefs/
+
+    // <package_name>_preferences.xml
+    DialerUtils.getDefaultSharedPreferenceForDeviceProtectedStorageContext(application);
+  }
+
+  private static boolean isStrictModeAllowed() {
+    return BuildType.get() == BuildType.BUGFOOD;
+  }
+
+  /**
+   * Set the recommended policy for the app.
+   *
+   * @param threadPenalties policy with preferred penalties. Detection bits will be ignored.
+   */
+  private static void setRecommendedMainThreadPolicy(StrictMode.ThreadPolicy threadPenalties) {
+    StrictMode.ThreadPolicy threadPolicy =
+        new StrictMode.ThreadPolicy.Builder(threadPenalties).detectAll().build();
+    StrictMode.setThreadPolicy(threadPolicy);
+  }
+
+  /**
+   * Set the recommended policy for the app.
+   *
+   * @param vmPenalties policy with preferred penalties. Detection bits should be unset.
+   */
+  private static void setRecommendedVMPolicy(StrictMode.VmPolicy vmPenalties) {
+    setRecommendedVMPolicy(vmPenalties, StrictModeVmConfig.empty());
+  }
+
+  /**
+   * Set the recommended policy for the app.
+   *
+   * @param vmPenalties policy with preferred penalties. Detection bits should be unset.
+   */
+  private static void setRecommendedVMPolicy(
+      StrictMode.VmPolicy vmPenalties, StrictModeVmConfig config) {
+    Assert.isNotNull(config);
+    StrictMode.VmPolicy.Builder vmPolicyBuilder =
+        new StrictMode.VmPolicy.Builder(vmPenalties)
+            .detectLeakedClosableObjects()
+            .detectLeakedSqlLiteObjects();
+    if (Build.VERSION.SDK_INT >= 26) {
+      vmPolicyBuilder.detectContentUriWithoutPermission();
+      // TODO(azlatin): Enable detecting untagged sockets once: b/64840386 is fixed.
+      // vmPolicyBuilder.detectUntaggedSockets();
+    }
+    StrictMode.setVmPolicy(vmPolicyBuilder.build());
+  }
+
+  /** VmPolicy configuration. */
+  @AutoValue
+  abstract static class StrictModeVmConfig {
+    /** A map of a class to the maximum number of allowed instances of that class. */
+    @Nullable
+    abstract Map<Class<?>, Integer> maxInstanceLimits();
+
+    public static StrictModeVmConfig empty() {
+      return builder().build();
+    }
+
+    public static Builder builder() {
+      return new AutoValue_SystemDialerStrictMode_StrictModeVmConfig.Builder();
+    }
+
+    /** VmPolicy configuration builder. */
+    @AutoValue.Builder
+    public abstract static class Builder {
+      public abstract Builder setMaxInstanceLimits(Map<Class<?>, Integer> limits);
+
+      public abstract StrictModeVmConfig build();
+
+      Builder() {}
+    }
+
+    StrictModeVmConfig() {}
+  }
+}
diff --git a/java/com/android/dialer/strictmode/impl/SystemStrictModeModule.java b/java/com/android/dialer/strictmode/impl/SystemStrictModeModule.java
new file mode 100644
index 0000000..6ece874
--- /dev/null
+++ b/java/com/android/dialer/strictmode/impl/SystemStrictModeModule.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.dialer.strictmode.impl;
+
+import com.android.dialer.strictmode.DialerStrictMode;
+import dagger.Binds;
+import dagger.Module;
+import javax.inject.Singleton;
+
+/** Module which binds {@link SystemDialerStrictMode}. */
+@Module
+public abstract class SystemStrictModeModule {
+
+  @Binds
+  @Singleton
+  public abstract DialerStrictMode bindDialerStrictMode(SystemDialerStrictMode impl);
+}
diff --git a/java/com/android/incallui/CallerInfoAsyncQuery.java b/java/com/android/incallui/CallerInfoAsyncQuery.java
index 86b1b7f..09752c7 100644
--- a/java/com/android/incallui/CallerInfoAsyncQuery.java
+++ b/java/com/android/incallui/CallerInfoAsyncQuery.java
@@ -41,7 +41,7 @@
 import com.android.dialer.phonenumbercache.CachedNumberLookupService.CachedContactInfo;
 import com.android.dialer.phonenumbercache.ContactInfoHelper;
 import com.android.dialer.phonenumbercache.PhoneNumberCache;
-import com.android.dialer.strictmode.DialerStrictMode;
+import com.android.dialer.strictmode.StrictModeUtils;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
@@ -191,7 +191,7 @@
       CallerInfo info,
       OnQueryCompleteListener listener,
       Object cookie) {
-    long[] directoryIds = DialerStrictMode.bypass(() -> getDirectoryIds(context));
+    long[] directoryIds = StrictModeUtils.bypass(() -> getDirectoryIds(context));
     int size = directoryIds.length;
     if (size == 0) {
       return false;
diff --git a/java/com/android/incallui/calllocation/impl/GoogleLocationSettingHelper.java b/java/com/android/incallui/calllocation/impl/GoogleLocationSettingHelper.java
index ed2e843..305ab43 100644
--- a/java/com/android/incallui/calllocation/impl/GoogleLocationSettingHelper.java
+++ b/java/com/android/incallui/calllocation/impl/GoogleLocationSettingHelper.java
@@ -26,7 +26,7 @@
 import android.provider.Settings.Secure;
 import android.provider.Settings.SettingNotFoundException;
 import com.android.dialer.common.LogUtil;
-import com.android.dialer.strictmode.DialerStrictMode;
+import com.android.dialer.strictmode.StrictModeUtils;
 
 /**
  * Helper class to check if Google Location Services is enabled. This class is based on
@@ -121,7 +121,7 @@
     if (!isEnforceable(context)) {
       return true;
     }
-    int locationServiceStatus = DialerStrictMode.bypass(() -> getUseLocationForServices(context));
+    int locationServiceStatus = StrictModeUtils.bypass(() -> getUseLocationForServices(context));
     return locationServiceStatus == USE_LOCATION_FOR_SERVICES_ON;
   }
 }
diff --git a/java/com/android/incallui/incall/impl/InCallFragment.java b/java/com/android/incallui/incall/impl/InCallFragment.java
index d91b5f2..1317565 100644
--- a/java/com/android/incallui/incall/impl/InCallFragment.java
+++ b/java/com/android/incallui/incall/impl/InCallFragment.java
@@ -44,7 +44,7 @@
 import com.android.dialer.logging.DialerImpression;
 import com.android.dialer.logging.Logger;
 import com.android.dialer.multimedia.MultimediaData;
-import com.android.dialer.strictmode.DialerStrictMode;
+import com.android.dialer.strictmode.StrictModeUtils;
 import com.android.dialer.widget.LockableViewPager;
 import com.android.incallui.audioroute.AudioRouteSelectorDialogFragment;
 import com.android.incallui.audioroute.AudioRouteSelectorDialogFragment.AudioRouteSelectorPresenter;
@@ -142,7 +142,7 @@
     LogUtil.i("InCallFragment.onCreateView", null);
     // Bypass to avoid StrictModeResourceMismatchViolation
     final View view =
-        DialerStrictMode.bypass(
+        StrictModeUtils.bypass(
             () -> layoutInflater.inflate(R.layout.frag_incall_voice, viewGroup, false));
     contactGridManager =
         new ContactGridManager(
diff --git a/java/com/android/voicemail/impl/VoicemailStatus.java b/java/com/android/voicemail/impl/VoicemailStatus.java
index 5553cf5..a24bad4 100644
--- a/java/com/android/voicemail/impl/VoicemailStatus.java
+++ b/java/com/android/voicemail/impl/VoicemailStatus.java
@@ -24,7 +24,7 @@
 import android.provider.VoicemailContract.Status;
 import android.support.annotation.Nullable;
 import android.telecom.PhoneAccountHandle;
-import com.android.dialer.strictmode.DialerStrictMode;
+import com.android.dialer.strictmode.StrictModeUtils;
 
 public class VoicemailStatus {
 
@@ -100,7 +100,7 @@
       ContentResolver contentResolver = mContext.getContentResolver();
       Uri statusUri = VoicemailContract.Status.buildSourceUri(mContext.getPackageName());
       try {
-        DialerStrictMode.bypass(() -> contentResolver.insert(statusUri, mValues));
+        StrictModeUtils.bypass(() -> contentResolver.insert(statusUri, mValues));
       } catch (IllegalArgumentException iae) {
         VvmLog.e(TAG, "apply :: failed to insert content resolver ", iae);
         mValues.clear();
diff --git a/java/com/android/voicemail/impl/configui/ConfigOverrideFragment.java b/java/com/android/voicemail/impl/configui/ConfigOverrideFragment.java
index caf33df..d05940a 100644
--- a/java/com/android/voicemail/impl/configui/ConfigOverrideFragment.java
+++ b/java/com/android/voicemail/impl/configui/ConfigOverrideFragment.java
@@ -35,7 +35,7 @@
 import android.text.TextUtils;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.concurrent.ThreadUtil;
-import com.android.dialer.strictmode.DialerStrictMode;
+import com.android.dialer.strictmode.StrictModeUtils;
 import com.android.voicemail.VoicemailComponent;
 
 /**
@@ -128,7 +128,7 @@
   }
 
   public static boolean isOverridden(Context context) {
-    return DialerStrictMode.bypass(
+    return StrictModeUtils.bypass(
         () ->
             PreferenceManager.getDefaultSharedPreferences(context)
                 .getBoolean(context.getString(R.string.vvm_config_override_enabled_key), false));
diff --git a/java/com/android/voicemail/impl/scheduling/TaskSchedulerJobService.java b/java/com/android/voicemail/impl/scheduling/TaskSchedulerJobService.java
index baf5804..0e3f27c 100644
--- a/java/com/android/voicemail/impl/scheduling/TaskSchedulerJobService.java
+++ b/java/com/android/voicemail/impl/scheduling/TaskSchedulerJobService.java
@@ -30,7 +30,7 @@
 import android.preference.PreferenceManager;
 import android.support.annotation.MainThread;
 import com.android.dialer.constants.ScheduledJobIds;
-import com.android.dialer.strictmode.DialerStrictMode;
+import com.android.dialer.strictmode.StrictModeUtils;
 import com.android.voicemail.impl.Assert;
 import com.android.voicemail.impl.VvmLog;
 import java.util.ArrayList;
@@ -59,7 +59,7 @@
   public boolean onStartJob(JobParameters params) {
     int jobId = params.getTransientExtras().getInt(EXTRA_JOB_ID);
     int expectedJobId =
-        DialerStrictMode.bypass(
+        StrictModeUtils.bypass(
             () -> PreferenceManager.getDefaultSharedPreferences(this).getInt(EXPECTED_JOB_ID, 0));
     if (jobId != expectedJobId) {
       VvmLog.e(