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