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 package com.android.dialer.configprovider; 17 18 import android.app.IntentService; 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.SharedPreferences; 22 import android.content.SharedPreferences.Editor; 23 import android.os.StrictMode; 24 import android.support.annotation.Nullable; 25 import com.android.dialer.common.Assert; 26 import com.android.dialer.common.LogUtil; 27 import com.android.dialer.inject.ApplicationContext; 28 import com.android.dialer.util.DialerUtils; 29 import javax.inject.Inject; 30 31 /** 32 * {@link ConfigProvider} which uses a shared preferences file. 33 * 34 * <p>Config flags can be written using adb (with root access), for example: 35 * 36 * <pre> 37 * adb root 38 * adb shell am startservice -n \ 39 * 'com.android.dialer/.configprovider.SharedPrefConfigProvider\$Service' \ 40 * --ez boolean_flag_name flag_value 41 * </pre> 42 * 43 * <p>(For longs use --el and for strings use --es.) 44 * 45 * <p>Flags can be viewed with: 46 * 47 * <pre> 48 * adb shell cat \ 49 * /data/user_de/0/com.android.dialer/shared_prefs/com.android.dialer_preferences.xml 50 * </pre> 51 */ 52 class SharedPrefConfigProvider implements ConfigProvider { 53 private static final String PREF_PREFIX = "config_provider_prefs_"; 54 55 private final Context appContext; 56 57 @Inject SharedPrefConfigProvider(@pplicationContext Context appContext)58 SharedPrefConfigProvider(@ApplicationContext Context appContext) { 59 this.appContext = appContext; 60 } 61 62 /** Service to write values into {@link SharedPrefConfigProvider} using adb. */ 63 public static class Service extends IntentService { 64 Service()65 public Service() { 66 super("SharedPrefConfigProvider.Service"); 67 } 68 69 @Override onHandleIntent(@ullable Intent intent)70 protected void onHandleIntent(@Nullable Intent intent) { 71 if (intent == null || intent.getExtras() == null || intent.getExtras().size() != 1) { 72 LogUtil.w("SharedPrefConfigProvider.Service.onHandleIntent", "must set exactly one extra"); 73 return; 74 } 75 String key = intent.getExtras().keySet().iterator().next(); 76 Object value = intent.getExtras().get(key); 77 put(key, value); 78 } 79 put(String key, Object value)80 private void put(String key, Object value) { 81 Editor editor = getSharedPrefs(getApplicationContext()).edit(); 82 String prefixedKey = PREF_PREFIX + key; 83 if (value instanceof Boolean) { 84 editor.putBoolean(prefixedKey, (Boolean) value); 85 } else if (value instanceof Long) { 86 editor.putLong(prefixedKey, (Long) value); 87 } else if (value instanceof String) { 88 editor.putString(prefixedKey, (String) value); 89 } else { 90 throw Assert.createAssertionFailException("unsupported extra type: " + value.getClass()); 91 } 92 editor.apply(); 93 } 94 } 95 96 @Override getString(String key, String defaultValue)97 public String getString(String key, String defaultValue) { 98 return bypassStrictMode( 99 () -> getSharedPrefs(appContext).getString(PREF_PREFIX + key, defaultValue)); 100 } 101 102 @Override getLong(String key, long defaultValue)103 public long getLong(String key, long defaultValue) { 104 return bypassStrictMode( 105 () -> getSharedPrefs(appContext).getLong(PREF_PREFIX + key, defaultValue)); 106 } 107 108 @Override getBoolean(String key, boolean defaultValue)109 public boolean getBoolean(String key, boolean defaultValue) { 110 return bypassStrictMode( 111 () -> getSharedPrefs(appContext).getBoolean(PREF_PREFIX + key, defaultValue)); 112 } 113 getSharedPrefs(Context appContext)114 private static SharedPreferences getSharedPrefs(Context appContext) { 115 return DialerUtils.getDefaultSharedPreferenceForDeviceProtectedStorageContext(appContext); 116 } 117 118 private interface Provider<T> { get()119 T get(); 120 } 121 122 // Reading shared prefs on the main thread is generally safe since a single instance is cached. bypassStrictMode(Provider<T> provider)123 private static <T> T bypassStrictMode(Provider<T> provider) { 124 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); 125 try { 126 return provider.get(); 127 } finally { 128 StrictMode.setThreadPolicy(oldPolicy); 129 } 130 } 131 } 132