blob: e7a271cbbb8eb8e283f367a815db611af902d0a1 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.os;
18
Andreas Gampea90534b2017-07-29 14:14:39 -070019import android.annotation.NonNull;
20import android.annotation.Nullable;
Sundong Ahn5e05a9a2018-01-03 19:16:01 +090021import android.annotation.SystemApi;
Philip P. Moltmannf80809f2018-04-04 11:20:44 -070022import android.annotation.TestApi;
Andrei Onea24ec3212019-03-15 17:35:05 +000023import android.annotation.UnsupportedAppUsage;
John Reckaa67f682016-09-20 14:24:21 -070024import android.util.Log;
25import android.util.MutableInt;
26
27import com.android.internal.annotations.GuardedBy;
28
Daniel Colascione6e2cff72019-11-14 13:22:31 -080029import dalvik.annotation.optimization.CriticalNative;
Daniel Colascione5e8ba5f2019-11-14 01:46:37 -080030import dalvik.annotation.optimization.FastNative;
31
Jeff Sharkeyb0c363b22018-12-15 11:53:03 -070032import libcore.util.HexEncoding;
Dianne Hackborna53de062012-05-08 18:53:51 -070033
Jeff Sharkeyb0c363b22018-12-15 11:53:03 -070034import java.nio.charset.StandardCharsets;
35import java.security.MessageDigest;
36import java.security.NoSuchAlgorithmException;
37import java.util.ArrayList;
38import java.util.Arrays;
39import java.util.HashMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040
41/**
42 * Gives access to the system properties store. The system properties
43 * store contains a list of string key-value pairs.
44 *
Jiyong Park09c730b2019-09-21 14:03:09 +090045 * <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 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 * {@hide}
53 */
Sundong Ahn5e05a9a2018-01-03 19:16:01 +090054@SystemApi
Philip P. Moltmannf80809f2018-04-04 11:20:44 -070055@TestApi
John Reckaa67f682016-09-20 14:24:21 -070056public class SystemProperties {
57 private static final String TAG = "SystemProperties";
58 private static final boolean TRACK_KEY_ACCESS = false;
59
Elliott Hughes70cfefa2017-03-15 13:01:53 -070060 /**
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).
Sundong Ahn5e05a9a2018-01-03 19:16:01 +090063 * @hide
Elliott Hughes70cfefa2017-03-15 13:01:53 -070064 */
Andrei Onea24ec3212019-03-15 17:35:05 +000065 @UnsupportedAppUsage
Elliott Hughes70cfefa2017-03-15 13:01:53 -070066 public static final int PROP_NAME_MAX = Integer.MAX_VALUE;
67
Sundong Ahn5e05a9a2018-01-03 19:16:01 +090068 /** @hide */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069 public static final int PROP_VALUE_MAX = 91;
70
Andrei Onea24ec3212019-03-15 17:35:05 +000071 @UnsupportedAppUsage
Andreas Gampea90534b2017-07-29 14:14:39 -070072 @GuardedBy("sChangeCallbacks")
Dianne Hackborna53de062012-05-08 18:53:51 -070073 private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>();
74
John Reckaa67f682016-09-20 14:24:21 -070075 @GuardedBy("sRoReads")
Andreas Gampea90534b2017-07-29 14:14:39 -070076 private static final HashMap<String, MutableInt> sRoReads =
77 TRACK_KEY_ACCESS ? new HashMap<>() : null;
John Reckaa67f682016-09-20 14:24:21 -070078
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
Daniel Colascione5e8ba5f2019-11-14 01:46:37 -080099 // 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.
Andrei Onea24ec3212019-03-15 17:35:05 +0000103 @UnsupportedAppUsage
Daniel Colascione5e8ba5f2019-11-14 01:46:37 -0800104 private static String native_get(String key) {
105 return native_get(key, "");
106 }
107
108 @FastNative
Artur Satayev70507ed2019-07-29 13:18:27 +0100109 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 private static native String native_get(String key, String def);
Daniel Colascione5e8ba5f2019-11-14 01:46:37 -0800111 @FastNative
Artur Satayev70507ed2019-07-29 13:18:27 +0100112 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Mike Lockwoodd1945952009-08-12 17:15:51 -0400113 private static native int native_get_int(String key, int def);
Daniel Colascione5e8ba5f2019-11-14 01:46:37 -0800114 @FastNative
Andrei Onea24ec3212019-03-15 17:35:05 +0000115 @UnsupportedAppUsage
Mike Lockwoodd1945952009-08-12 17:15:51 -0400116 private static native long native_get_long(String key, long def);
Daniel Colascione5e8ba5f2019-11-14 01:46:37 -0800117 @FastNative
Artur Satayev70507ed2019-07-29 13:18:27 +0100118 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Mike Lockwoodd1945952009-08-12 17:15:51 -0400119 private static native boolean native_get_boolean(String key, boolean def);
Daniel Colascione5e8ba5f2019-11-14 01:46:37 -0800120
Daniel Colascione6e2cff72019-11-14 13:22:31 -0800121 @FastNative
122 private static native long native_find(String name);
123 @FastNative
124 private static native String native_get(long handle);
125 @CriticalNative
126 private static native int native_get_int(long handle, int def);
127 @CriticalNative
128 private static native long native_get_long(long handle, long def);
129 @CriticalNative
130 private static native boolean native_get_boolean(long handle, boolean def);
131
Daniel Colascione5e8ba5f2019-11-14 01:46:37 -0800132 // _NOT_ FastNative: native_set performs IPC and can block
Artur Satayev70507ed2019-07-29 13:18:27 +0100133 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 private static native void native_set(String key, String def);
Daniel Colascione5e8ba5f2019-11-14 01:46:37 -0800135
Artur Satayev70507ed2019-07-29 13:18:27 +0100136 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Dianne Hackborna53de062012-05-08 18:53:51 -0700137 private static native void native_add_change_callback();
Martijn Coenen0754b272016-11-17 14:06:38 +0100138 private static native void native_report_sysprop_change();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139
140 /**
Andreas Gampe33aea8d2017-07-28 18:20:37 -0700141 * Get the String value for the given {@code key}.
142 *
Andreas Gampea90534b2017-07-29 14:14:39 -0700143 * @param key the key to lookup
Andreas Gampe33aea8d2017-07-28 18:20:37 -0700144 * @return an empty string if the {@code key} isn't found
Sundong Ahn5e05a9a2018-01-03 19:16:01 +0900145 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 */
Andreas Gampea90534b2017-07-29 14:14:39 -0700147 @NonNull
Sundong Ahn5e05a9a2018-01-03 19:16:01 +0900148 @SystemApi
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600149 @TestApi
Andreas Gampea90534b2017-07-29 14:14:39 -0700150 public static String get(@NonNull String key) {
John Reckaa67f682016-09-20 14:24:21 -0700151 if (TRACK_KEY_ACCESS) onKeyAccess(key);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 return native_get(key);
153 }
154
155 /**
Andreas Gampe33aea8d2017-07-28 18:20:37 -0700156 * Get the String value for the given {@code key}.
157 *
Andreas Gampea90534b2017-07-29 14:14:39 -0700158 * @param key the key to lookup
159 * @param def the default value in case the property is not set or empty
Andreas Gampe33aea8d2017-07-28 18:20:37 -0700160 * @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty
161 * string otherwise
Sundong Ahn5e05a9a2018-01-03 19:16:01 +0900162 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 */
Andreas Gampea90534b2017-07-29 14:14:39 -0700164 @NonNull
Sundong Ahn5e05a9a2018-01-03 19:16:01 +0900165 @SystemApi
Philip P. Moltmannf80809f2018-04-04 11:20:44 -0700166 @TestApi
Andreas Gampea90534b2017-07-29 14:14:39 -0700167 public static String get(@NonNull String key, @Nullable String def) {
John Reckaa67f682016-09-20 14:24:21 -0700168 if (TRACK_KEY_ACCESS) onKeyAccess(key);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 return native_get(key, def);
170 }
171
172 /**
Andreas Gampea90534b2017-07-29 14:14:39 -0700173 * Get the value for the given {@code key}, and return as an integer.
174 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 * @param key the key to lookup
176 * @param def a default value to return
177 * @return the key parsed as an integer, or def if the key isn't found or
178 * cannot be parsed
Sundong Ahn5e05a9a2018-01-03 19:16:01 +0900179 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 */
Sundong Ahn5e05a9a2018-01-03 19:16:01 +0900181 @SystemApi
Sasha Kuznetsov3a81af12019-10-29 11:29:34 -0700182 @TestApi
Andreas Gampea90534b2017-07-29 14:14:39 -0700183 public static int getInt(@NonNull String key, int def) {
John Reckaa67f682016-09-20 14:24:21 -0700184 if (TRACK_KEY_ACCESS) onKeyAccess(key);
Mike Lockwoodd1945952009-08-12 17:15:51 -0400185 return native_get_int(key, def);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 }
187
188 /**
Andreas Gampea90534b2017-07-29 14:14:39 -0700189 * Get the value for the given {@code key}, and return as a long.
190 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 * @param key the key to lookup
192 * @param def a default value to return
193 * @return the key parsed as a long, or def if the key isn't found or
194 * cannot be parsed
Sundong Ahn5e05a9a2018-01-03 19:16:01 +0900195 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 */
Sundong Ahn5e05a9a2018-01-03 19:16:01 +0900197 @SystemApi
Sasha Kuznetsov3a81af12019-10-29 11:29:34 -0700198 @TestApi
Andreas Gampea90534b2017-07-29 14:14:39 -0700199 public static long getLong(@NonNull String key, long def) {
John Reckaa67f682016-09-20 14:24:21 -0700200 if (TRACK_KEY_ACCESS) onKeyAccess(key);
Mike Lockwoodd1945952009-08-12 17:15:51 -0400201 return native_get_long(key, def);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 }
203
204 /**
Andreas Gampea90534b2017-07-29 14:14:39 -0700205 * Get the value for the given {@code key}, returned as a boolean.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 * Values 'n', 'no', '0', 'false' or 'off' are considered false.
207 * Values 'y', 'yes', '1', 'true' or 'on' are considered true.
Brad Fitzpatrickc1a968a2010-11-24 08:56:40 -0800208 * (case sensitive).
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 * If the key does not exist, or has any other value, then the default
210 * result is returned.
Andreas Gampea90534b2017-07-29 14:14:39 -0700211 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 * @param key the key to lookup
213 * @param def a default value to return
214 * @return the key parsed as a boolean, or def if the key isn't found or is
215 * not able to be parsed as a boolean.
Sundong Ahn5e05a9a2018-01-03 19:16:01 +0900216 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 */
Sundong Ahn5e05a9a2018-01-03 19:16:01 +0900218 @SystemApi
Howard Chena7e62a82019-05-24 02:04:50 +0800219 @TestApi
Andreas Gampea90534b2017-07-29 14:14:39 -0700220 public static boolean getBoolean(@NonNull String key, boolean def) {
John Reckaa67f682016-09-20 14:24:21 -0700221 if (TRACK_KEY_ACCESS) onKeyAccess(key);
Mike Lockwoodd1945952009-08-12 17:15:51 -0400222 return native_get_boolean(key, def);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 }
224
225 /**
Andreas Gampea90534b2017-07-29 14:14:39 -0700226 * Set the value for the given {@code key} to {@code val}.
227 *
228 * @throws IllegalArgumentException if the {@code val} exceeds 91 characters
Tom Cherry39373b22019-09-19 08:28:27 -0700229 * @throws RuntimeException if the property cannot be set, for example, if it was blocked by
230 * SELinux. libc will log the underlying reason.
Sundong Ahn5e05a9a2018-01-03 19:16:01 +0900231 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 */
Andrei Onea24ec3212019-03-15 17:35:05 +0000233 @UnsupportedAppUsage
Andreas Gampea90534b2017-07-29 14:14:39 -0700234 public static void set(@NonNull String key, @Nullable String val) {
Tom Cherry38a77c42017-10-18 09:25:17 -0700235 if (val != null && !val.startsWith("ro.") && val.length() > PROP_VALUE_MAX) {
Andreas Gampea90534b2017-07-29 14:14:39 -0700236 throw new IllegalArgumentException("value of system property '" + key
237 + "' is longer than " + PROP_VALUE_MAX + " characters: " + val);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 }
John Reckaa67f682016-09-20 14:24:21 -0700239 if (TRACK_KEY_ACCESS) onKeyAccess(key);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 native_set(key, val);
241 }
Dianne Hackborna53de062012-05-08 18:53:51 -0700242
Andreas Gampea90534b2017-07-29 14:14:39 -0700243 /**
244 * Add a callback that will be run whenever any system property changes.
245 *
246 * @param callback The {@link Runnable} that should be executed when a system property
247 * changes.
Sundong Ahn5e05a9a2018-01-03 19:16:01 +0900248 * @hide
Andreas Gampea90534b2017-07-29 14:14:39 -0700249 */
Andrei Onea24ec3212019-03-15 17:35:05 +0000250 @UnsupportedAppUsage
Andreas Gampea90534b2017-07-29 14:14:39 -0700251 public static void addChangeCallback(@NonNull Runnable callback) {
Dianne Hackborna53de062012-05-08 18:53:51 -0700252 synchronized (sChangeCallbacks) {
253 if (sChangeCallbacks.size() == 0) {
254 native_add_change_callback();
255 }
256 sChangeCallbacks.add(callback);
257 }
258 }
259
lindatsenge07b3e62019-04-02 16:09:34 -0700260 /**
261 * Remove the target callback.
262 *
263 * @param callback The {@link Runnable} that should be removed.
264 * @hide
265 */
266 @UnsupportedAppUsage
267 public static void removeChangeCallback(@NonNull Runnable callback) {
268 synchronized (sChangeCallbacks) {
269 if (sChangeCallbacks.contains(callback)) {
270 sChangeCallbacks.remove(callback);
271 }
272 }
273 }
274
Andreas Gampea90534b2017-07-29 14:14:39 -0700275 @SuppressWarnings("unused") // Called from native code.
276 private static void callChangeCallbacks() {
Daniel Colascione5e8ba5f2019-11-14 01:46:37 -0800277 ArrayList<Runnable> callbacks = null;
Dianne Hackborna53de062012-05-08 18:53:51 -0700278 synchronized (sChangeCallbacks) {
279 //Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!");
280 if (sChangeCallbacks.size() == 0) {
281 return;
282 }
Daniel Colascione5e8ba5f2019-11-14 01:46:37 -0800283 callbacks = new ArrayList<Runnable>(sChangeCallbacks);
284 }
285 final long token = Binder.clearCallingIdentity();
286 try {
287 for (int i = 0; i < callbacks.size(); i++) {
288 try {
289 callbacks.get(i).run();
290 } catch (Throwable t) {
291 // Ignore and try to go on. Don't use wtf here: that
292 // will cause the process to exit on some builds and break tests.
293 Log.e(TAG, "Exception in SystemProperties change callback", t);
Andreas Gampe92fc0652018-03-16 16:14:29 -0700294 }
Dianne Hackborna53de062012-05-08 18:53:51 -0700295 }
Daniel Colascione5e8ba5f2019-11-14 01:46:37 -0800296 } finally {
297 Binder.restoreCallingIdentity(token);
Dianne Hackborna53de062012-05-08 18:53:51 -0700298 }
299 }
Martijn Coenen0754b272016-11-17 14:06:38 +0100300
Sundong Ahn5e05a9a2018-01-03 19:16:01 +0900301 /**
Martijn Coenen0754b272016-11-17 14:06:38 +0100302 * Notifies listeners that a system property has changed
Sundong Ahn5e05a9a2018-01-03 19:16:01 +0900303 * @hide
Martijn Coenen0754b272016-11-17 14:06:38 +0100304 */
Andrei Onea24ec3212019-03-15 17:35:05 +0000305 @UnsupportedAppUsage
Martijn Coenen0754b272016-11-17 14:06:38 +0100306 public static void reportSyspropChanged() {
307 native_report_sysprop_change();
308 }
Sundong Ahn5e05a9a2018-01-03 19:16:01 +0900309
Jeff Sharkeyb0c363b22018-12-15 11:53:03 -0700310 /**
311 * Return a {@code SHA-1} digest of the given keys and their values as a
312 * hex-encoded string. The ordering of the incoming keys doesn't change the
313 * digest result.
314 *
315 * @hide
316 */
317 public static @NonNull String digestOf(@NonNull String... keys) {
318 Arrays.sort(keys);
319 try {
320 final MessageDigest digest = MessageDigest.getInstance("SHA-1");
321 for (String key : keys) {
322 final String item = key + "=" + get(key) + "\n";
323 digest.update(item.getBytes(StandardCharsets.UTF_8));
324 }
325 return HexEncoding.encodeToString(digest.digest()).toLowerCase();
326 } catch (NoSuchAlgorithmException e) {
327 throw new RuntimeException(e);
328 }
329 }
330
Andrei Onea24ec3212019-03-15 17:35:05 +0000331 @UnsupportedAppUsage
Sundong Ahn5e05a9a2018-01-03 19:16:01 +0900332 private SystemProperties() {
333 }
Daniel Colascione6e2cff72019-11-14 13:22:31 -0800334
335 /**
336 * Look up a property location by name.
337 * @name name of the property
338 * @return property handle or {@code null} if property isn't set
339 * @hide
340 */
341 @Nullable public static Handle find(@NonNull String name) {
342 long nativeHandle = native_find(name);
343 if (nativeHandle == 0) {
344 return null;
345 }
346 return new Handle(nativeHandle);
347 }
348
349 /**
350 * Handle to a pre-located property. Looking up a property handle in advance allows
351 * for optimal repeated lookup of a single property.
352 * @hide
353 */
354 public static final class Handle {
355
356 private final long mNativeHandle;
357
358 /**
359 * @return Value of the property
360 */
361 @NonNull public String get() {
362 return native_get(mNativeHandle);
363 }
364 /**
365 * @param def default value
366 * @return value or {@code def} on parse error
367 */
368 public int getInt(int def) {
369 return native_get_int(mNativeHandle, def);
370 }
371 /**
372 * @param def default value
373 * @return value or {@code def} on parse error
374 */
375 public long getLong(long def) {
376 return native_get_long(mNativeHandle, def);
377 }
378 /**
379 * @param def default value
380 * @return value or {@code def} on parse error
381 */
382 public boolean getBoolean(boolean def) {
383 return native_get_boolean(mNativeHandle, def);
384 }
385
386 private Handle(long nativeHandle) {
387 mNativeHandle = nativeHandle;
388 }
389 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390}