• 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.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