1 /* 2 * Copyright (C) 2006 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 android.os; 18 19 import android.util.Log; 20 import android.util.MutableInt; 21 22 import com.android.internal.annotations.GuardedBy; 23 24 import java.util.ArrayList; 25 import java.util.HashMap; 26 27 28 /** 29 * Gives access to the system properties store. The system properties 30 * store contains a list of string key-value pairs. 31 * 32 * {@hide} 33 */ 34 public class SystemProperties { 35 private static final String TAG = "SystemProperties"; 36 private static final boolean TRACK_KEY_ACCESS = false; 37 38 /** 39 * Android O removed the property name length limit, but com.amazon.kindle 7.8.1.5 40 * uses reflection to read this whenever text is selected (http://b/36095274). 41 */ 42 public static final int PROP_NAME_MAX = Integer.MAX_VALUE; 43 44 public static final int PROP_VALUE_MAX = 91; 45 46 private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>(); 47 48 @GuardedBy("sRoReads") 49 private static final HashMap<String, MutableInt> sRoReads; 50 static { 51 if (TRACK_KEY_ACCESS) { 52 sRoReads = new HashMap<>(); 53 } else { 54 sRoReads = null; 55 } 56 } 57 onKeyAccess(String key)58 private static void onKeyAccess(String key) { 59 if (!TRACK_KEY_ACCESS) return; 60 61 if (key != null && key.startsWith("ro.")) { 62 synchronized (sRoReads) { 63 MutableInt numReads = sRoReads.getOrDefault(key, null); 64 if (numReads == null) { 65 numReads = new MutableInt(0); 66 sRoReads.put(key, numReads); 67 } 68 numReads.value++; 69 if (numReads.value > 3) { 70 Log.d(TAG, "Repeated read (count=" + numReads.value 71 + ") of a read-only system property '" + key + "'", 72 new Exception()); 73 } 74 } 75 } 76 } 77 native_get(String key)78 private static native String native_get(String key); native_get(String key, String def)79 private static native String native_get(String key, String def); native_get_int(String key, int def)80 private static native int native_get_int(String key, int def); native_get_long(String key, long def)81 private static native long native_get_long(String key, long def); native_get_boolean(String key, boolean def)82 private static native boolean native_get_boolean(String key, boolean def); native_set(String key, String def)83 private static native void native_set(String key, String def); native_add_change_callback()84 private static native void native_add_change_callback(); native_report_sysprop_change()85 private static native void native_report_sysprop_change(); 86 87 /** 88 * Get the value for the given key. 89 * @return an empty string if the key isn't found 90 */ get(String key)91 public static String get(String key) { 92 if (TRACK_KEY_ACCESS) onKeyAccess(key); 93 return native_get(key); 94 } 95 96 /** 97 * Get the value for the given key. 98 * @return if the key isn't found, return def if it isn't null, or an empty string otherwise 99 */ get(String key, String def)100 public static String get(String key, String def) { 101 if (TRACK_KEY_ACCESS) onKeyAccess(key); 102 return native_get(key, def); 103 } 104 105 /** 106 * Get the value for the given key, and return as an integer. 107 * @param key the key to lookup 108 * @param def a default value to return 109 * @return the key parsed as an integer, or def if the key isn't found or 110 * cannot be parsed 111 */ getInt(String key, int def)112 public static int getInt(String key, int def) { 113 if (TRACK_KEY_ACCESS) onKeyAccess(key); 114 return native_get_int(key, def); 115 } 116 117 /** 118 * Get the value for the given key, and return as a long. 119 * @param key the key to lookup 120 * @param def a default value to return 121 * @return the key parsed as a long, or def if the key isn't found or 122 * cannot be parsed 123 */ getLong(String key, long def)124 public static long getLong(String key, long def) { 125 if (TRACK_KEY_ACCESS) onKeyAccess(key); 126 return native_get_long(key, def); 127 } 128 129 /** 130 * Get the value for the given key, returned as a boolean. 131 * Values 'n', 'no', '0', 'false' or 'off' are considered false. 132 * Values 'y', 'yes', '1', 'true' or 'on' are considered true. 133 * (case sensitive). 134 * If the key does not exist, or has any other value, then the default 135 * result is returned. 136 * @param key the key to lookup 137 * @param def a default value to return 138 * @return the key parsed as a boolean, or def if the key isn't found or is 139 * not able to be parsed as a boolean. 140 */ getBoolean(String key, boolean def)141 public static boolean getBoolean(String key, boolean def) { 142 if (TRACK_KEY_ACCESS) onKeyAccess(key); 143 return native_get_boolean(key, def); 144 } 145 146 /** 147 * Set the value for the given key. 148 * @throws IllegalArgumentException if the value exceeds 92 characters 149 */ set(String key, String val)150 public static void set(String key, String val) { 151 if (val != null && val.length() > PROP_VALUE_MAX) { 152 throw newValueTooLargeException(key, val); 153 } 154 if (TRACK_KEY_ACCESS) onKeyAccess(key); 155 native_set(key, val); 156 } 157 addChangeCallback(Runnable callback)158 public static void addChangeCallback(Runnable callback) { 159 synchronized (sChangeCallbacks) { 160 if (sChangeCallbacks.size() == 0) { 161 native_add_change_callback(); 162 } 163 sChangeCallbacks.add(callback); 164 } 165 } 166 callChangeCallbacks()167 static void callChangeCallbacks() { 168 synchronized (sChangeCallbacks) { 169 //Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!"); 170 if (sChangeCallbacks.size() == 0) { 171 return; 172 } 173 ArrayList<Runnable> callbacks = new ArrayList<Runnable>(sChangeCallbacks); 174 for (int i=0; i<callbacks.size(); i++) { 175 callbacks.get(i).run(); 176 } 177 } 178 } 179 newValueTooLargeException(String key, String value)180 private static IllegalArgumentException newValueTooLargeException(String key, String value) { 181 return new IllegalArgumentException("value of system property '" + key + "' is longer than " 182 + PROP_VALUE_MAX + " characters: " + value); 183 } 184 185 /* 186 * Notifies listeners that a system property has changed 187 */ reportSyspropChanged()188 public static void reportSyspropChanged() { 189 native_report_sysprop_change(); 190 } 191 } 192