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