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