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