Home | History | Annotate | Download | only in strictmode
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.dialer.strictmode;
     18 
     19 import android.app.Application;
     20 import android.content.Context;
     21 import android.os.Looper;
     22 import android.os.StrictMode;
     23 import android.os.StrictMode.ThreadPolicy;
     24 import android.preference.PreferenceManager;
     25 import android.support.annotation.AnyThread;
     26 import android.support.v4.os.UserManagerCompat;
     27 import com.android.dialer.buildtype.BuildType;
     28 import com.android.dialer.buildtype.BuildType.Type;
     29 import com.android.dialer.function.Supplier;
     30 import com.android.dialer.storage.StorageComponent;
     31 
     32 /** Utilities for enforcing strict-mode in an app. */
     33 public final class StrictModeUtils {
     34 
     35   private static final ThreadPolicy THREAD_NO_PENALTY =
     36       new StrictMode.ThreadPolicy.Builder().permitAll().build();
     37 
     38   /**
     39    * Convenience method for disabling and enabling the thread policy death penalty using lambdas.
     40    *
     41    * <p>For example:
     42    *
     43    * <p><code>
     44    *   Value foo = StrictModeUtils.bypass(() -> doDiskAccessOnMainThreadReturningValue());
     45    * </code>
     46    *
     47    * <p>The thread policy is only mutated if this is called from the main thread.
     48    */
     49   @AnyThread
     50   public static <T> T bypass(Supplier<T> supplier) {
     51     if (isStrictModeAllowed() && onMainThread()) {
     52       ThreadPolicy originalPolicy = StrictMode.getThreadPolicy();
     53       StrictMode.setThreadPolicy(THREAD_NO_PENALTY);
     54       try {
     55         return supplier.get();
     56       } finally {
     57         StrictMode.setThreadPolicy(originalPolicy);
     58       }
     59     }
     60     return supplier.get();
     61   }
     62 
     63   /**
     64    * Convenience method for disabling and enabling the thread policy death penalty using lambdas.
     65    *
     66    * <p>For example:
     67    *
     68    * <p><code>
     69    *   StrictModeUtils.bypass(() -> doDiskAccessOnMainThread());
     70    * </code>
     71    *
     72    * <p>The thread policy is only mutated if this is called from the main thread.
     73    */
     74   @AnyThread
     75   public static void bypass(Runnable runnable) {
     76     if (isStrictModeAllowed() && onMainThread()) {
     77       ThreadPolicy originalPolicy = StrictMode.getThreadPolicy();
     78       StrictMode.setThreadPolicy(THREAD_NO_PENALTY);
     79       try {
     80         runnable.run();
     81       } finally {
     82         StrictMode.setThreadPolicy(originalPolicy);
     83       }
     84     } else {
     85       runnable.run();
     86     }
     87   }
     88 
     89   public static boolean isStrictModeAllowed() {
     90     return BuildType.get() == Type.BUGFOOD;
     91   }
     92 
     93   private static boolean onMainThread() {
     94     return Looper.getMainLooper().equals(Looper.myLooper());
     95   }
     96 
     97   /**
     98    * We frequently access shared preferences on the main thread, which causes strict mode
     99    * violations. When strict mode is allowed, warm up the shared preferences so that later uses of
    100    * shared preferences access the in-memory versions and we don't have to bypass strict mode at
    101    * every point in the application where shared preferences are accessed.
    102    */
    103   public static void warmupSharedPrefs(Application application) {
    104     // From credential-encrypted (CE) storage, i.e.:
    105     //    /data/data/com.android.dialer/shared_prefs
    106 
    107     if (UserManagerCompat.isUserUnlocked(application)) {
    108       // <package_name>_preferences.xml
    109       PreferenceManager.getDefaultSharedPreferences(application);
    110 
    111       // <package_name>.xml
    112       application.getSharedPreferences(application.getPackageName(), Context.MODE_PRIVATE);
    113     }
    114 
    115     // From device-encrypted (DE) storage, i.e.:
    116     //   /data/user_de/0/com.android.dialer/shared_prefs/
    117 
    118     // <package_name>_preferences.xml
    119     StorageComponent.get(application).unencryptedSharedPrefs();
    120   }
    121 
    122   private StrictModeUtils() {}
    123 }
    124