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.support.annotation.Nullable; 24 import com.android.dialer.common.Assert; 25 import com.android.dialer.common.LogUtil; 26 import com.android.dialer.storage.StorageComponent; 27 import com.android.dialer.storage.Unencrypted; 28 import com.android.dialer.strictmode.StrictModeUtils; 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 public class SharedPrefConfigProvider implements ConfigProvider { 53 private static final String PREF_PREFIX = "config_provider_prefs_"; 54 55 private final SharedPreferences sharedPreferences; 56 57 @Inject SharedPrefConfigProvider(@nencrypted SharedPreferences sharedPreferences)58 SharedPrefConfigProvider(@Unencrypted SharedPreferences sharedPreferences) { 59 this.sharedPreferences = sharedPreferences; 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 /** Set a boolean config value. */ putBoolean(String key, boolean value)97 public void putBoolean(String key, boolean value) { 98 sharedPreferences.edit().putBoolean(PREF_PREFIX + key, value).apply(); 99 } 100 putLong(String key, long value)101 public void putLong(String key, long value) { 102 sharedPreferences.edit().putLong(PREF_PREFIX + key, value).apply(); 103 } 104 putString(String key, String value)105 public void putString(String key, String value) { 106 sharedPreferences.edit().putString(PREF_PREFIX + key, value).apply(); 107 } 108 109 @Override getString(String key, String defaultValue)110 public String getString(String key, String defaultValue) { 111 // Reading shared prefs on the main thread is generally safe since a single instance is cached. 112 return StrictModeUtils.bypass( 113 () -> sharedPreferences.getString(PREF_PREFIX + key, defaultValue)); 114 } 115 116 @Override getLong(String key, long defaultValue)117 public long getLong(String key, long defaultValue) { 118 // Reading shared prefs on the main thread is generally safe since a single instance is cached. 119 return StrictModeUtils.bypass(() -> sharedPreferences.getLong(PREF_PREFIX + key, defaultValue)); 120 } 121 122 @Override getBoolean(String key, boolean defaultValue)123 public boolean getBoolean(String key, boolean defaultValue) { 124 // Reading shared prefs on the main thread is generally safe since a single instance is cached. 125 return StrictModeUtils.bypass( 126 () -> sharedPreferences.getBoolean(PREF_PREFIX + key, defaultValue)); 127 } 128 getSharedPrefs(Context appContext)129 private static SharedPreferences getSharedPrefs(Context appContext) { 130 return StorageComponent.get(appContext).unencryptedSharedPrefs(); 131 } 132 } 133