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
bypass(Supplier<T> supplier)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
bypass(Runnable runnable)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 
isStrictModeAllowed()89   public static boolean isStrictModeAllowed() {
90     return BuildType.get() == Type.BUGFOOD;
91   }
92 
onMainThread()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    */
warmupSharedPrefs(Application application)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 
StrictModeUtils()122   private StrictModeUtils() {}
123 }
124