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.annotation.TestApi;
23 import android.util.Log;
24 import android.util.MutableInt;
25 
26 import com.android.internal.annotations.GuardedBy;
27 
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 
31 
32 /**
33  * Gives access to the system properties store.  The system properties
34  * store contains a list of string key-value pairs.
35  *
36  * {@hide}
37  */
38 @SystemApi
39 @TestApi
40 public class SystemProperties {
41     private static final String TAG = "SystemProperties";
42     private static final boolean TRACK_KEY_ACCESS = false;
43 
44     /**
45      * Android O removed the property name length limit, but com.amazon.kindle 7.8.1.5
46      * uses reflection to read this whenever text is selected (http://b/36095274).
47      * @hide
48      */
49     public static final int PROP_NAME_MAX = Integer.MAX_VALUE;
50 
51     /** @hide */
52     public static final int PROP_VALUE_MAX = 91;
53 
54     @GuardedBy("sChangeCallbacks")
55     private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>();
56 
57     @GuardedBy("sRoReads")
58     private static final HashMap<String, MutableInt> sRoReads =
59             TRACK_KEY_ACCESS ? new HashMap<>() : null;
60 
onKeyAccess(String key)61     private static void onKeyAccess(String key) {
62         if (!TRACK_KEY_ACCESS) return;
63 
64         if (key != null && key.startsWith("ro.")) {
65             synchronized (sRoReads) {
66                 MutableInt numReads = sRoReads.getOrDefault(key, null);
67                 if (numReads == null) {
68                     numReads = new MutableInt(0);
69                     sRoReads.put(key, numReads);
70                 }
71                 numReads.value++;
72                 if (numReads.value > 3) {
73                     Log.d(TAG, "Repeated read (count=" + numReads.value
74                             + ") of a read-only system property '" + key + "'",
75                             new Exception());
76                 }
77             }
78         }
79     }
80 
native_get(String key)81     private static native String native_get(String key);
native_get(String key, String def)82     private static native String native_get(String key, String def);
native_get_int(String key, int def)83     private static native int native_get_int(String key, int def);
native_get_long(String key, long def)84     private static native long native_get_long(String key, long def);
native_get_boolean(String key, boolean def)85     private static native boolean native_get_boolean(String key, boolean def);
native_set(String key, String def)86     private static native void native_set(String key, String def);
native_add_change_callback()87     private static native void native_add_change_callback();
native_report_sysprop_change()88     private static native void native_report_sysprop_change();
89 
90     /**
91      * Get the String value for the given {@code key}.
92      *
93      * @param key the key to lookup
94      * @return an empty string if the {@code key} isn't found
95      * @hide
96      */
97     @NonNull
98     @SystemApi
get(@onNull String key)99     public static String get(@NonNull String key) {
100         if (TRACK_KEY_ACCESS) onKeyAccess(key);
101         return native_get(key);
102     }
103 
104     /**
105      * Get the String value for the given {@code key}.
106      *
107      * @param key the key to lookup
108      * @param def the default value in case the property is not set or empty
109      * @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty
110      * string otherwise
111      * @hide
112      */
113     @NonNull
114     @SystemApi
115     @TestApi
get(@onNull String key, @Nullable String def)116     public static String get(@NonNull String key, @Nullable String def) {
117         if (TRACK_KEY_ACCESS) onKeyAccess(key);
118         return native_get(key, def);
119     }
120 
121     /**
122      * Get the value for the given {@code key}, and return as an integer.
123      *
124      * @param key the key to lookup
125      * @param def a default value to return
126      * @return the key parsed as an integer, or def if the key isn't found or
127      *         cannot be parsed
128      * @hide
129      */
130     @SystemApi
getInt(@onNull String key, int def)131     public static int getInt(@NonNull String key, int def) {
132         if (TRACK_KEY_ACCESS) onKeyAccess(key);
133         return native_get_int(key, def);
134     }
135 
136     /**
137      * Get the value for the given {@code key}, and return as a long.
138      *
139      * @param key the key to lookup
140      * @param def a default value to return
141      * @return the key parsed as a long, or def if the key isn't found or
142      *         cannot be parsed
143      * @hide
144      */
145     @SystemApi
getLong(@onNull String key, long def)146     public static long getLong(@NonNull String key, long def) {
147         if (TRACK_KEY_ACCESS) onKeyAccess(key);
148         return native_get_long(key, def);
149     }
150 
151     /**
152      * Get the value for the given {@code key}, returned as a boolean.
153      * Values 'n', 'no', '0', 'false' or 'off' are considered false.
154      * Values 'y', 'yes', '1', 'true' or 'on' are considered true.
155      * (case sensitive).
156      * If the key does not exist, or has any other value, then the default
157      * result is returned.
158      *
159      * @param key the key to lookup
160      * @param def a default value to return
161      * @return the key parsed as a boolean, or def if the key isn't found or is
162      *         not able to be parsed as a boolean.
163      * @hide
164      */
165     @SystemApi
getBoolean(@onNull String key, boolean def)166     public static boolean getBoolean(@NonNull String key, boolean def) {
167         if (TRACK_KEY_ACCESS) onKeyAccess(key);
168         return native_get_boolean(key, def);
169     }
170 
171     /**
172      * Set the value for the given {@code key} to {@code val}.
173      *
174      * @throws IllegalArgumentException if the {@code val} exceeds 91 characters
175      * @hide
176      */
set(@onNull String key, @Nullable String val)177     public static void set(@NonNull String key, @Nullable String val) {
178         if (val != null && !val.startsWith("ro.") && val.length() > PROP_VALUE_MAX) {
179             throw new IllegalArgumentException("value of system property '" + key
180                     + "' is longer than " + PROP_VALUE_MAX + " characters: " + val);
181         }
182         if (TRACK_KEY_ACCESS) onKeyAccess(key);
183         native_set(key, val);
184     }
185 
186     /**
187      * Add a callback that will be run whenever any system property changes.
188      *
189      * @param callback The {@link Runnable} that should be executed when a system property
190      * changes.
191      * @hide
192      */
addChangeCallback(@onNull Runnable callback)193     public static void addChangeCallback(@NonNull Runnable callback) {
194         synchronized (sChangeCallbacks) {
195             if (sChangeCallbacks.size() == 0) {
196                 native_add_change_callback();
197             }
198             sChangeCallbacks.add(callback);
199         }
200     }
201 
202     @SuppressWarnings("unused")  // Called from native code.
callChangeCallbacks()203     private static void callChangeCallbacks() {
204         synchronized (sChangeCallbacks) {
205             //Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!");
206             if (sChangeCallbacks.size() == 0) {
207                 return;
208             }
209             ArrayList<Runnable> callbacks = new ArrayList<Runnable>(sChangeCallbacks);
210             for (int i=0; i<callbacks.size(); i++) {
211                 try {
212                     callbacks.get(i).run();
213                 } catch (Throwable t) {
214                     Log.wtf(TAG, "Exception in SystemProperties change callback", t);
215                     // Ignore and try to go on.
216                 }
217             }
218         }
219     }
220 
221     /**
222      * Notifies listeners that a system property has changed
223      * @hide
224      */
reportSyspropChanged()225     public static void reportSyspropChanged() {
226         native_report_sysprop_change();
227     }
228 
SystemProperties()229     private SystemProperties() {
230     }
231 }
232