The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 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 | |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 19 | import android.annotation.NonNull; |
| 20 | import android.annotation.Nullable; |
Sundong Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 21 | import android.annotation.SystemApi; |
Philip P. Moltmann | f80809f | 2018-04-04 11:20:44 -0700 | [diff] [blame] | 22 | import android.annotation.TestApi; |
Artur Satayev | afdb23a | 2019-12-10 17:47:53 +0000 | [diff] [blame] | 23 | import android.compat.annotation.UnsupportedAppUsage; |
John Reck | aa67f68 | 2016-09-20 14:24:21 -0700 | [diff] [blame] | 24 | import android.util.Log; |
| 25 | import android.util.MutableInt; |
| 26 | |
| 27 | import com.android.internal.annotations.GuardedBy; |
| 28 | |
Daniel Colascione | 6e2cff7 | 2019-11-14 13:22:31 -0800 | [diff] [blame] | 29 | import dalvik.annotation.optimization.CriticalNative; |
Daniel Colascione | 5e8ba5f | 2019-11-14 01:46:37 -0800 | [diff] [blame] | 30 | import dalvik.annotation.optimization.FastNative; |
| 31 | |
Jeff Sharkey | b0c363b2 | 2018-12-15 11:53:03 -0700 | [diff] [blame] | 32 | import libcore.util.HexEncoding; |
Dianne Hackborn | a53de06 | 2012-05-08 18:53:51 -0700 | [diff] [blame] | 33 | |
Jeff Sharkey | b0c363b2 | 2018-12-15 11:53:03 -0700 | [diff] [blame] | 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; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 40 | |
| 41 | /** |
| 42 | * Gives access to the system properties store. The system properties |
| 43 | * store contains a list of string key-value pairs. |
| 44 | * |
Jiyong Park | 09c730b | 2019-09-21 14:03:09 +0900 | [diff] [blame] | 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 | * |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 52 | * {@hide} |
| 53 | */ |
Sundong Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 54 | @SystemApi |
Philip P. Moltmann | f80809f | 2018-04-04 11:20:44 -0700 | [diff] [blame] | 55 | @TestApi |
John Reck | aa67f68 | 2016-09-20 14:24:21 -0700 | [diff] [blame] | 56 | public class SystemProperties { |
| 57 | private static final String TAG = "SystemProperties"; |
| 58 | private static final boolean TRACK_KEY_ACCESS = false; |
| 59 | |
Elliott Hughes | 70cfefa | 2017-03-15 13:01:53 -0700 | [diff] [blame] | 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). |
Sundong Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 63 | * @hide |
Elliott Hughes | 70cfefa | 2017-03-15 13:01:53 -0700 | [diff] [blame] | 64 | */ |
Andrei Onea | 24ec321 | 2019-03-15 17:35:05 +0000 | [diff] [blame] | 65 | @UnsupportedAppUsage |
Elliott Hughes | 70cfefa | 2017-03-15 13:01:53 -0700 | [diff] [blame] | 66 | public static final int PROP_NAME_MAX = Integer.MAX_VALUE; |
| 67 | |
Sundong Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 68 | /** @hide */ |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 69 | public static final int PROP_VALUE_MAX = 91; |
| 70 | |
Andrei Onea | 24ec321 | 2019-03-15 17:35:05 +0000 | [diff] [blame] | 71 | @UnsupportedAppUsage |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 72 | @GuardedBy("sChangeCallbacks") |
Dianne Hackborn | a53de06 | 2012-05-08 18:53:51 -0700 | [diff] [blame] | 73 | private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>(); |
| 74 | |
John Reck | aa67f68 | 2016-09-20 14:24:21 -0700 | [diff] [blame] | 75 | @GuardedBy("sRoReads") |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 76 | private static final HashMap<String, MutableInt> sRoReads = |
| 77 | TRACK_KEY_ACCESS ? new HashMap<>() : null; |
John Reck | aa67f68 | 2016-09-20 14:24:21 -0700 | [diff] [blame] | 78 | |
| 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 Colascione | 5e8ba5f | 2019-11-14 01:46:37 -0800 | [diff] [blame] | 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. |
Andrei Onea | 24ec321 | 2019-03-15 17:35:05 +0000 | [diff] [blame] | 103 | @UnsupportedAppUsage |
Daniel Colascione | 5e8ba5f | 2019-11-14 01:46:37 -0800 | [diff] [blame] | 104 | private static String native_get(String key) { |
| 105 | return native_get(key, ""); |
| 106 | } |
| 107 | |
| 108 | @FastNative |
Artur Satayev | 70507ed | 2019-07-29 13:18:27 +0100 | [diff] [blame] | 109 | @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 110 | private static native String native_get(String key, String def); |
Daniel Colascione | 5e8ba5f | 2019-11-14 01:46:37 -0800 | [diff] [blame] | 111 | @FastNative |
Artur Satayev | 70507ed | 2019-07-29 13:18:27 +0100 | [diff] [blame] | 112 | @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) |
Mike Lockwood | d194595 | 2009-08-12 17:15:51 -0400 | [diff] [blame] | 113 | private static native int native_get_int(String key, int def); |
Daniel Colascione | 5e8ba5f | 2019-11-14 01:46:37 -0800 | [diff] [blame] | 114 | @FastNative |
Andrei Onea | 24ec321 | 2019-03-15 17:35:05 +0000 | [diff] [blame] | 115 | @UnsupportedAppUsage |
Mike Lockwood | d194595 | 2009-08-12 17:15:51 -0400 | [diff] [blame] | 116 | private static native long native_get_long(String key, long def); |
Daniel Colascione | 5e8ba5f | 2019-11-14 01:46:37 -0800 | [diff] [blame] | 117 | @FastNative |
Artur Satayev | 70507ed | 2019-07-29 13:18:27 +0100 | [diff] [blame] | 118 | @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) |
Mike Lockwood | d194595 | 2009-08-12 17:15:51 -0400 | [diff] [blame] | 119 | private static native boolean native_get_boolean(String key, boolean def); |
Daniel Colascione | 5e8ba5f | 2019-11-14 01:46:37 -0800 | [diff] [blame] | 120 | |
Daniel Colascione | 6e2cff7 | 2019-11-14 13:22:31 -0800 | [diff] [blame] | 121 | @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 Colascione | 5e8ba5f | 2019-11-14 01:46:37 -0800 | [diff] [blame] | 132 | // _NOT_ FastNative: native_set performs IPC and can block |
Artur Satayev | 70507ed | 2019-07-29 13:18:27 +0100 | [diff] [blame] | 133 | @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 134 | private static native void native_set(String key, String def); |
Daniel Colascione | 5e8ba5f | 2019-11-14 01:46:37 -0800 | [diff] [blame] | 135 | |
Artur Satayev | 70507ed | 2019-07-29 13:18:27 +0100 | [diff] [blame] | 136 | @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) |
Dianne Hackborn | a53de06 | 2012-05-08 18:53:51 -0700 | [diff] [blame] | 137 | private static native void native_add_change_callback(); |
Martijn Coenen | 0754b27 | 2016-11-17 14:06:38 +0100 | [diff] [blame] | 138 | private static native void native_report_sysprop_change(); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 139 | |
| 140 | /** |
Andreas Gampe | 33aea8d | 2017-07-28 18:20:37 -0700 | [diff] [blame] | 141 | * Get the String value for the given {@code key}. |
| 142 | * |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 143 | * @param key the key to lookup |
Andreas Gampe | 33aea8d | 2017-07-28 18:20:37 -0700 | [diff] [blame] | 144 | * @return an empty string if the {@code key} isn't found |
Sundong Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 145 | * @hide |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 146 | */ |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 147 | @NonNull |
Sundong Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 148 | @SystemApi |
Jeff Sharkey | c609116 | 2018-06-29 17:15:40 -0600 | [diff] [blame] | 149 | @TestApi |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 150 | public static String get(@NonNull String key) { |
John Reck | aa67f68 | 2016-09-20 14:24:21 -0700 | [diff] [blame] | 151 | if (TRACK_KEY_ACCESS) onKeyAccess(key); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 152 | return native_get(key); |
| 153 | } |
| 154 | |
| 155 | /** |
Andreas Gampe | 33aea8d | 2017-07-28 18:20:37 -0700 | [diff] [blame] | 156 | * Get the String value for the given {@code key}. |
| 157 | * |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 158 | * @param key the key to lookup |
| 159 | * @param def the default value in case the property is not set or empty |
Andreas Gampe | 33aea8d | 2017-07-28 18:20:37 -0700 | [diff] [blame] | 160 | * @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty |
| 161 | * string otherwise |
Sundong Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 162 | * @hide |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 163 | */ |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 164 | @NonNull |
Sundong Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 165 | @SystemApi |
Philip P. Moltmann | f80809f | 2018-04-04 11:20:44 -0700 | [diff] [blame] | 166 | @TestApi |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 167 | public static String get(@NonNull String key, @Nullable String def) { |
John Reck | aa67f68 | 2016-09-20 14:24:21 -0700 | [diff] [blame] | 168 | if (TRACK_KEY_ACCESS) onKeyAccess(key); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 169 | return native_get(key, def); |
| 170 | } |
| 171 | |
| 172 | /** |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 173 | * Get the value for the given {@code key}, and return as an integer. |
| 174 | * |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 175 | * @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 Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 179 | * @hide |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 180 | */ |
Sundong Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 181 | @SystemApi |
Sasha Kuznetsov | 3a81af1 | 2019-10-29 11:29:34 -0700 | [diff] [blame] | 182 | @TestApi |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 183 | public static int getInt(@NonNull String key, int def) { |
John Reck | aa67f68 | 2016-09-20 14:24:21 -0700 | [diff] [blame] | 184 | if (TRACK_KEY_ACCESS) onKeyAccess(key); |
Mike Lockwood | d194595 | 2009-08-12 17:15:51 -0400 | [diff] [blame] | 185 | return native_get_int(key, def); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 186 | } |
| 187 | |
| 188 | /** |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 189 | * Get the value for the given {@code key}, and return as a long. |
| 190 | * |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 191 | * @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 Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 195 | * @hide |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 196 | */ |
Sundong Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 197 | @SystemApi |
Sasha Kuznetsov | 3a81af1 | 2019-10-29 11:29:34 -0700 | [diff] [blame] | 198 | @TestApi |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 199 | public static long getLong(@NonNull String key, long def) { |
John Reck | aa67f68 | 2016-09-20 14:24:21 -0700 | [diff] [blame] | 200 | if (TRACK_KEY_ACCESS) onKeyAccess(key); |
Mike Lockwood | d194595 | 2009-08-12 17:15:51 -0400 | [diff] [blame] | 201 | return native_get_long(key, def); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 202 | } |
| 203 | |
| 204 | /** |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 205 | * Get the value for the given {@code key}, returned as a boolean. |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 206 | * Values 'n', 'no', '0', 'false' or 'off' are considered false. |
| 207 | * Values 'y', 'yes', '1', 'true' or 'on' are considered true. |
Brad Fitzpatrick | c1a968a | 2010-11-24 08:56:40 -0800 | [diff] [blame] | 208 | * (case sensitive). |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 209 | * If the key does not exist, or has any other value, then the default |
| 210 | * result is returned. |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 211 | * |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 212 | * @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 Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 216 | * @hide |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 217 | */ |
Sundong Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 218 | @SystemApi |
Howard Chen | a7e62a8 | 2019-05-24 02:04:50 +0800 | [diff] [blame] | 219 | @TestApi |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 220 | public static boolean getBoolean(@NonNull String key, boolean def) { |
John Reck | aa67f68 | 2016-09-20 14:24:21 -0700 | [diff] [blame] | 221 | if (TRACK_KEY_ACCESS) onKeyAccess(key); |
Mike Lockwood | d194595 | 2009-08-12 17:15:51 -0400 | [diff] [blame] | 222 | return native_get_boolean(key, def); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 223 | } |
| 224 | |
| 225 | /** |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 226 | * Set the value for the given {@code key} to {@code val}. |
| 227 | * |
| 228 | * @throws IllegalArgumentException if the {@code val} exceeds 91 characters |
Tom Cherry | 39373b2 | 2019-09-19 08:28:27 -0700 | [diff] [blame] | 229 | * @throws RuntimeException if the property cannot be set, for example, if it was blocked by |
| 230 | * SELinux. libc will log the underlying reason. |
Sundong Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 231 | * @hide |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 232 | */ |
Andrei Onea | 24ec321 | 2019-03-15 17:35:05 +0000 | [diff] [blame] | 233 | @UnsupportedAppUsage |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 234 | public static void set(@NonNull String key, @Nullable String val) { |
Tom Cherry | 38a77c4 | 2017-10-18 09:25:17 -0700 | [diff] [blame] | 235 | if (val != null && !val.startsWith("ro.") && val.length() > PROP_VALUE_MAX) { |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 236 | throw new IllegalArgumentException("value of system property '" + key |
| 237 | + "' is longer than " + PROP_VALUE_MAX + " characters: " + val); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 238 | } |
John Reck | aa67f68 | 2016-09-20 14:24:21 -0700 | [diff] [blame] | 239 | if (TRACK_KEY_ACCESS) onKeyAccess(key); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 240 | native_set(key, val); |
| 241 | } |
Dianne Hackborn | a53de06 | 2012-05-08 18:53:51 -0700 | [diff] [blame] | 242 | |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 243 | /** |
| 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 Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 248 | * @hide |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 249 | */ |
Andrei Onea | 24ec321 | 2019-03-15 17:35:05 +0000 | [diff] [blame] | 250 | @UnsupportedAppUsage |
Andreas Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 251 | public static void addChangeCallback(@NonNull Runnable callback) { |
Dianne Hackborn | a53de06 | 2012-05-08 18:53:51 -0700 | [diff] [blame] | 252 | synchronized (sChangeCallbacks) { |
| 253 | if (sChangeCallbacks.size() == 0) { |
| 254 | native_add_change_callback(); |
| 255 | } |
| 256 | sChangeCallbacks.add(callback); |
| 257 | } |
| 258 | } |
| 259 | |
lindatseng | e07b3e6 | 2019-04-02 16:09:34 -0700 | [diff] [blame] | 260 | /** |
| 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 Gampe | a90534b | 2017-07-29 14:14:39 -0700 | [diff] [blame] | 275 | @SuppressWarnings("unused") // Called from native code. |
| 276 | private static void callChangeCallbacks() { |
Daniel Colascione | 5e8ba5f | 2019-11-14 01:46:37 -0800 | [diff] [blame] | 277 | ArrayList<Runnable> callbacks = null; |
Dianne Hackborn | a53de06 | 2012-05-08 18:53:51 -0700 | [diff] [blame] | 278 | synchronized (sChangeCallbacks) { |
| 279 | //Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!"); |
| 280 | if (sChangeCallbacks.size() == 0) { |
| 281 | return; |
| 282 | } |
Daniel Colascione | 5e8ba5f | 2019-11-14 01:46:37 -0800 | [diff] [blame] | 283 | 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 Gampe | 92fc065 | 2018-03-16 16:14:29 -0700 | [diff] [blame] | 294 | } |
Dianne Hackborn | a53de06 | 2012-05-08 18:53:51 -0700 | [diff] [blame] | 295 | } |
Daniel Colascione | 5e8ba5f | 2019-11-14 01:46:37 -0800 | [diff] [blame] | 296 | } finally { |
| 297 | Binder.restoreCallingIdentity(token); |
Dianne Hackborn | a53de06 | 2012-05-08 18:53:51 -0700 | [diff] [blame] | 298 | } |
| 299 | } |
Martijn Coenen | 0754b27 | 2016-11-17 14:06:38 +0100 | [diff] [blame] | 300 | |
Sundong Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 301 | /** |
Martijn Coenen | 0754b27 | 2016-11-17 14:06:38 +0100 | [diff] [blame] | 302 | * Notifies listeners that a system property has changed |
Sundong Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 303 | * @hide |
Martijn Coenen | 0754b27 | 2016-11-17 14:06:38 +0100 | [diff] [blame] | 304 | */ |
Andrei Onea | 24ec321 | 2019-03-15 17:35:05 +0000 | [diff] [blame] | 305 | @UnsupportedAppUsage |
Martijn Coenen | 0754b27 | 2016-11-17 14:06:38 +0100 | [diff] [blame] | 306 | public static void reportSyspropChanged() { |
| 307 | native_report_sysprop_change(); |
| 308 | } |
Sundong Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 309 | |
Jeff Sharkey | b0c363b2 | 2018-12-15 11:53:03 -0700 | [diff] [blame] | 310 | /** |
| 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 Onea | 24ec321 | 2019-03-15 17:35:05 +0000 | [diff] [blame] | 331 | @UnsupportedAppUsage |
Sundong Ahn | 5e05a9a | 2018-01-03 19:16:01 +0900 | [diff] [blame] | 332 | private SystemProperties() { |
| 333 | } |
Daniel Colascione | 6e2cff7 | 2019-11-14 13:22:31 -0800 | [diff] [blame] | 334 | |
| 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 Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 390 | } |