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