• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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