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.compat.annotation.UnsupportedAppUsage; 23 import android.util.Log; 24 import android.util.MutableInt; 25 26 import com.android.internal.annotations.GuardedBy; 27 28 import dalvik.annotation.optimization.CriticalNative; 29 import dalvik.annotation.optimization.FastNative; 30 31 import libcore.util.HexEncoding; 32 33 import java.nio.charset.StandardCharsets; 34 import java.security.MessageDigest; 35 import java.security.NoSuchAlgorithmException; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.HashMap; 39 40 /** 41 * Gives access to the system properties store. The system properties 42 * store contains a list of string key-value pairs. 43 * 44 * <p>Use this class only for the system properties that are local. e.g., within 45 * an app, a partition, or a module. For system properties used across the 46 * boundaries, formally define them in <code>*.sysprop</code> files and use the 47 * auto-generated methods. For more information, see <a href= 48 * "https://source.android.com/devices/architecture/sysprops-apis">Implementing 49 * System Properties as APIs</a>.</p> 50 * 51 * {@hide} 52 */ 53 @SystemApi 54 public class SystemProperties { 55 private static final String TAG = "SystemProperties"; 56 private static final boolean TRACK_KEY_ACCESS = false; 57 58 /** 59 * Android O removed the property name length limit, but com.amazon.kindle 7.8.1.5 60 * uses reflection to read this whenever text is selected (http://b/36095274). 61 * @hide 62 */ 63 @UnsupportedAppUsage(trackingBug = 172649311) 64 public static final int PROP_NAME_MAX = Integer.MAX_VALUE; 65 66 /** @hide */ 67 public static final int PROP_VALUE_MAX = 91; 68 69 @UnsupportedAppUsage 70 @GuardedBy("sChangeCallbacks") 71 private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>(); 72 73 @GuardedBy("sRoReads") 74 private static final HashMap<String, MutableInt> sRoReads = 75 TRACK_KEY_ACCESS ? new HashMap<>() : null; 76 onKeyAccess(String key)77 private static void onKeyAccess(String key) { 78 if (!TRACK_KEY_ACCESS) return; 79 80 if (key != null && key.startsWith("ro.")) { 81 synchronized (sRoReads) { 82 MutableInt numReads = sRoReads.getOrDefault(key, null); 83 if (numReads == null) { 84 numReads = new MutableInt(0); 85 sRoReads.put(key, numReads); 86 } 87 numReads.value++; 88 if (numReads.value > 3) { 89 Log.d(TAG, "Repeated read (count=" + numReads.value 90 + ") of a read-only system property '" + key + "'", 91 new Exception()); 92 } 93 } 94 } 95 } 96 97 // The one-argument version of native_get used to be a regular native function. Nowadays, 98 // we use the two-argument form of native_get all the time, but we can't just delete the 99 // one-argument overload: apps use it via reflection, as the UnsupportedAppUsage annotation 100 // indicates. Let's just live with having a Java function with a very unusual name. 101 @UnsupportedAppUsage native_get(String key)102 private static String native_get(String key) { 103 return native_get(key, ""); 104 } 105 106 @FastNative 107 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) native_get(String key, String def)108 private static native String native_get(String key, String def); 109 @FastNative 110 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) native_get_int(String key, int def)111 private static native int native_get_int(String key, int def); 112 @FastNative 113 @UnsupportedAppUsage native_get_long(String key, long def)114 private static native long native_get_long(String key, long def); 115 @FastNative 116 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) native_get_boolean(String key, boolean def)117 private static native boolean native_get_boolean(String key, boolean def); 118 119 @FastNative native_find(String name)120 private static native long native_find(String name); 121 @FastNative native_get(long handle)122 private static native String native_get(long handle); 123 @CriticalNative native_get_int(long handle, int def)124 private static native int native_get_int(long handle, int def); 125 @CriticalNative native_get_long(long handle, long def)126 private static native long native_get_long(long handle, long def); 127 @CriticalNative native_get_boolean(long handle, boolean def)128 private static native boolean native_get_boolean(long handle, boolean def); 129 130 // _NOT_ FastNative: native_set performs IPC and can block 131 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) native_set(String key, String def)132 private static native void native_set(String key, String def); 133 134 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) native_add_change_callback()135 private static native void native_add_change_callback(); native_report_sysprop_change()136 private static native void native_report_sysprop_change(); 137 138 /** 139 * Get the String value for the given {@code key}. 140 * 141 * @param key the key to lookup 142 * @return an empty string if the {@code key} isn't found 143 * @hide 144 */ 145 @NonNull 146 @SystemApi get(@onNull String key)147 public static String get(@NonNull String key) { 148 if (TRACK_KEY_ACCESS) onKeyAccess(key); 149 return native_get(key); 150 } 151 152 /** 153 * Get the String value for the given {@code key}. 154 * 155 * @param key the key to lookup 156 * @param def the default value in case the property is not set or empty 157 * @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty 158 * string otherwise 159 * @hide 160 */ 161 @NonNull 162 @SystemApi get(@onNull String key, @Nullable String def)163 public static String get(@NonNull String key, @Nullable String def) { 164 if (TRACK_KEY_ACCESS) onKeyAccess(key); 165 return native_get(key, def); 166 } 167 168 /** 169 * Get the value for the given {@code key}, and return as an integer. 170 * 171 * @param key the key to lookup 172 * @param def a default value to return 173 * @return the key parsed as an integer, or def if the key isn't found or 174 * cannot be parsed 175 * @hide 176 */ 177 @SystemApi getInt(@onNull String key, int def)178 public static int getInt(@NonNull String key, int def) { 179 if (TRACK_KEY_ACCESS) onKeyAccess(key); 180 return native_get_int(key, def); 181 } 182 183 /** 184 * Get the value for the given {@code key}, and return as a long. 185 * 186 * @param key the key to lookup 187 * @param def a default value to return 188 * @return the key parsed as a long, or def if the key isn't found or 189 * cannot be parsed 190 * @hide 191 */ 192 @SystemApi getLong(@onNull String key, long def)193 public static long getLong(@NonNull String key, long def) { 194 if (TRACK_KEY_ACCESS) onKeyAccess(key); 195 return native_get_long(key, def); 196 } 197 198 /** 199 * Get the value for the given {@code key}, returned as a boolean. 200 * Values 'n', 'no', '0', 'false' or 'off' are considered false. 201 * Values 'y', 'yes', '1', 'true' or 'on' are considered true. 202 * (case sensitive). 203 * If the key does not exist, or has any other value, then the default 204 * result is returned. 205 * 206 * @param key the key to lookup 207 * @param def a default value to return 208 * @return the key parsed as a boolean, or def if the key isn't found or is 209 * not able to be parsed as a boolean. 210 * @hide 211 */ 212 @SystemApi getBoolean(@onNull String key, boolean def)213 public static boolean getBoolean(@NonNull String key, boolean def) { 214 if (TRACK_KEY_ACCESS) onKeyAccess(key); 215 return native_get_boolean(key, def); 216 } 217 218 /** 219 * Set the value for the given {@code key} to {@code val}. 220 * 221 * @throws IllegalArgumentException for non read-only properties if the {@code val} exceeds 222 * 91 characters 223 * @throws RuntimeException if the property cannot be set, for example, if it was blocked by 224 * SELinux. libc will log the underlying reason. 225 * @hide 226 */ 227 @UnsupportedAppUsage set(@onNull String key, @Nullable String val)228 public static void set(@NonNull String key, @Nullable String val) { 229 if (val != null && !key.startsWith("ro.") && val.getBytes(StandardCharsets.UTF_8).length 230 > PROP_VALUE_MAX) { 231 throw new IllegalArgumentException("value of system property '" + key 232 + "' is longer than " + PROP_VALUE_MAX + " bytes: " + val); 233 } 234 if (TRACK_KEY_ACCESS) onKeyAccess(key); 235 native_set(key, val); 236 } 237 238 /** 239 * Add a callback that will be run whenever any system property changes. 240 * 241 * @param callback The {@link Runnable} that should be executed when a system property 242 * changes. 243 * @hide 244 */ 245 @UnsupportedAppUsage addChangeCallback(@onNull Runnable callback)246 public static void addChangeCallback(@NonNull Runnable callback) { 247 synchronized (sChangeCallbacks) { 248 if (sChangeCallbacks.size() == 0) { 249 native_add_change_callback(); 250 } 251 sChangeCallbacks.add(callback); 252 } 253 } 254 255 /** 256 * Remove the target callback. 257 * 258 * @param callback The {@link Runnable} that should be removed. 259 * @hide 260 */ 261 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) removeChangeCallback(@onNull Runnable callback)262 public static void removeChangeCallback(@NonNull Runnable callback) { 263 synchronized (sChangeCallbacks) { 264 if (sChangeCallbacks.contains(callback)) { 265 sChangeCallbacks.remove(callback); 266 } 267 } 268 } 269 270 @SuppressWarnings("unused") // Called from native code. callChangeCallbacks()271 private static void callChangeCallbacks() { 272 ArrayList<Runnable> callbacks = null; 273 synchronized (sChangeCallbacks) { 274 //Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!"); 275 if (sChangeCallbacks.size() == 0) { 276 return; 277 } 278 callbacks = new ArrayList<Runnable>(sChangeCallbacks); 279 } 280 final long token = Binder.clearCallingIdentity(); 281 try { 282 for (int i = 0; i < callbacks.size(); i++) { 283 try { 284 callbacks.get(i).run(); 285 } catch (Throwable t) { 286 // Ignore and try to go on. Don't use wtf here: that 287 // will cause the process to exit on some builds and break tests. 288 Log.e(TAG, "Exception in SystemProperties change callback", t); 289 } 290 } 291 } finally { 292 Binder.restoreCallingIdentity(token); 293 } 294 } 295 296 /** 297 * Notifies listeners that a system property has changed 298 * @hide 299 */ 300 @UnsupportedAppUsage reportSyspropChanged()301 public static void reportSyspropChanged() { 302 native_report_sysprop_change(); 303 } 304 305 /** 306 * Return a {@code SHA-1} digest of the given keys and their values as a 307 * hex-encoded string. The ordering of the incoming keys doesn't change the 308 * digest result. 309 * 310 * @hide 311 */ digestOf(@onNull String... keys)312 public static @NonNull String digestOf(@NonNull String... keys) { 313 Arrays.sort(keys); 314 try { 315 final MessageDigest digest = MessageDigest.getInstance("SHA-1"); 316 for (String key : keys) { 317 final String item = key + "=" + get(key) + "\n"; 318 digest.update(item.getBytes(StandardCharsets.UTF_8)); 319 } 320 return HexEncoding.encodeToString(digest.digest()).toLowerCase(); 321 } catch (NoSuchAlgorithmException e) { 322 throw new RuntimeException(e); 323 } 324 } 325 326 @UnsupportedAppUsage SystemProperties()327 private SystemProperties() { 328 } 329 330 /** 331 * Look up a property location by name. 332 * @name name of the property 333 * @return property handle or {@code null} if property isn't set 334 * @hide 335 */ find(@onNull String name)336 @Nullable public static Handle find(@NonNull String name) { 337 long nativeHandle = native_find(name); 338 if (nativeHandle == 0) { 339 return null; 340 } 341 return new Handle(nativeHandle); 342 } 343 344 /** 345 * Handle to a pre-located property. Looking up a property handle in advance allows 346 * for optimal repeated lookup of a single property. 347 * @hide 348 */ 349 public static final class Handle { 350 351 private final long mNativeHandle; 352 353 /** 354 * @return Value of the property 355 */ get()356 @NonNull public String get() { 357 return native_get(mNativeHandle); 358 } 359 /** 360 * @param def default value 361 * @return value or {@code def} on parse error 362 */ getInt(int def)363 public int getInt(int def) { 364 return native_get_int(mNativeHandle, def); 365 } 366 /** 367 * @param def default value 368 * @return value or {@code def} on parse error 369 */ getLong(long def)370 public long getLong(long def) { 371 return native_get_long(mNativeHandle, def); 372 } 373 /** 374 * @param def default value 375 * @return value or {@code def} on parse error 376 */ getBoolean(boolean def)377 public boolean getBoolean(boolean def) { 378 return native_get_boolean(mNativeHandle, def); 379 } 380 Handle(long nativeHandle)381 private Handle(long nativeHandle) { 382 mNativeHandle = nativeHandle; 383 } 384 } 385 } 386