Fix a bunch of repeated reads of a ro.* property

SystemProperties.get() is not particularly fast,
especially if a string is returned. Since ro.* values
are unable to be changed, there's no need to
continously re-query them. Cache the value at
static init time to trivially fix this.

Test: refactoring CL.
Change-Id: Iccb021d3cb2ba3a4a1d0048ddec6811bb7409eec
(cherry picked from commit aa67f684ff43c81e3280c846245ec6ebe907787e)
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index af981f6..e1ff383 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -32,6 +32,7 @@
 import android.os.ParcelFileDescriptor;
 
 import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.os.RoSystemProperties;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.util.FastPrintWriter;
 
@@ -895,7 +896,7 @@
 
     /** @hide */
     public static boolean isLowRamDeviceStatic() {
-        return "true".equals(SystemProperties.get("ro.config.low_ram", "false"));
+        return RoSystemProperties.CONFIG_LOW_RAM;
     }
 
     /**
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 27096b1..b56437e 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -18,6 +18,8 @@
 
 import android.os.SystemProperties;
 import android.util.Log;
+
+import com.android.internal.os.RoSystemProperties;
 import com.android.org.conscrypt.OpenSSLContextImpl;
 import com.android.org.conscrypt.OpenSSLSocketImpl;
 import com.android.org.conscrypt.SSLClientSessionCache;
@@ -221,8 +223,8 @@
     }
 
     private static boolean isSslCheckRelaxed() {
-        return "1".equals(SystemProperties.get("ro.debuggable")) &&
-            "yes".equals(SystemProperties.get("socket.relaxsslcheck"));
+        return RoSystemProperties.DEBUGGABLE &&
+            SystemProperties.getBoolean("socket.relaxsslcheck", false);
     }
 
     private synchronized SSLSocketFactory getDelegate() {
diff --git a/core/java/android/os/FactoryTest.java b/core/java/android/os/FactoryTest.java
index 7a252f9..b59227c 100644
--- a/core/java/android/os/FactoryTest.java
+++ b/core/java/android/os/FactoryTest.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import com.android.internal.os.RoSystemProperties;
+
 /**
  * Provides support for in-place factory test functions.
  *
@@ -36,7 +38,7 @@
      * or {@link #FACTORY_TEST_HIGH_LEVEL}.
      */
     public static int getMode() {
-        return SystemProperties.getInt("ro.factorytest", FACTORY_TEST_OFF);
+        return RoSystemProperties.FACTORYTEST;
     }
 
     /**
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index e47c238..6a751e8 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -16,7 +16,13 @@
 
 package android.os;
 
+import android.util.Log;
+import android.util.MutableInt;
+
+import com.android.internal.annotations.GuardedBy;
+
 import java.util.ArrayList;
+import java.util.HashMap;
 
 
 /**
@@ -25,13 +31,45 @@
  *
  * {@hide}
  */
-public class SystemProperties
-{
+public class SystemProperties {
+    private static final String TAG = "SystemProperties";
+    private static final boolean TRACK_KEY_ACCESS = false;
+
     public static final int PROP_NAME_MAX = 31;
     public static final int PROP_VALUE_MAX = 91;
 
     private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>();
 
+    @GuardedBy("sRoReads")
+    private static final HashMap<String, MutableInt> sRoReads;
+    static {
+        if (TRACK_KEY_ACCESS) {
+            sRoReads = new HashMap<>();
+        } else {
+            sRoReads = null;
+        }
+    }
+
+    private static void onKeyAccess(String key) {
+        if (!TRACK_KEY_ACCESS) return;
+
+        if (key != null && key.startsWith("ro.")) {
+            synchronized (sRoReads) {
+                MutableInt numReads = sRoReads.getOrDefault(key, null);
+                if (numReads == null) {
+                    numReads = new MutableInt(0);
+                    sRoReads.put(key, numReads);
+                }
+                numReads.value++;
+                if (numReads.value > 3) {
+                    Log.d(TAG, "Repeated read (count=" + numReads.value
+                            + ") of a read-only system property '" + key + "'",
+                            new Exception());
+                }
+            }
+        }
+    }
+
     private static native String native_get(String key);
     private static native String native_get(String key, String def);
     private static native int native_get_int(String key, int def);
@@ -50,6 +88,7 @@
         if (key.length() > PROP_NAME_MAX) {
             throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
         }
+        if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get(key);
     }
 
@@ -62,6 +101,7 @@
         if (key.length() > PROP_NAME_MAX) {
             throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
         }
+        if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get(key, def);
     }
 
@@ -77,6 +117,7 @@
         if (key.length() > PROP_NAME_MAX) {
             throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
         }
+        if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_int(key, def);
     }
 
@@ -92,6 +133,7 @@
         if (key.length() > PROP_NAME_MAX) {
             throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
         }
+        if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_long(key, def);
     }
 
@@ -112,6 +154,7 @@
         if (key.length() > PROP_NAME_MAX) {
             throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
         }
+        if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_boolean(key, def);
     }
 
@@ -128,6 +171,7 @@
             throw new IllegalArgumentException("val.length > " +
                 PROP_VALUE_MAX);
         }
+        if (TRACK_KEY_ACCESS) onKeyAccess(key);
         native_set(key, val);
     }
 
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index a4db940..ab462e4 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -42,6 +42,7 @@
 import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.R;
+import com.android.internal.os.RoSystemProperties;
 
 import java.io.IOException;
 import java.lang.annotation.Retention;
@@ -761,7 +762,7 @@
      * a single owner user.  see @link {android.os.UserHandle#USER_OWNER}
      */
     public static boolean isSplitSystemUser() {
-        return SystemProperties.getBoolean("ro.fw.system_user_split", false);
+        return RoSystemProperties.FW_SYSTEM_USER_SPLIT;
     }
 
     /**
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 9252887..63b6db0 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -44,6 +44,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.os.RoSystemProperties;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.Preconditions;
 
@@ -1167,8 +1168,7 @@
      *         false not encrypted and not encryptable
      */
     public static boolean isEncryptable() {
-        final String state = SystemProperties.get("ro.crypto.state", "unsupported");
-        return !"unsupported".equalsIgnoreCase(state);
+        return RoSystemProperties.CRYPTO_ENCRYPTABLE;
     }
 
     /** {@hide}
@@ -1177,8 +1177,7 @@
      *         false not encrypted
      */
     public static boolean isEncrypted() {
-        final String state = SystemProperties.get("ro.crypto.state", "");
-        return "encrypted".equalsIgnoreCase(state);
+        return RoSystemProperties.CRYPTO_ENCRYPTED;
     }
 
     /** {@hide}
@@ -1190,9 +1189,7 @@
         if (!isEncrypted()) {
             return false;
         }
-
-        final String status = SystemProperties.get("ro.crypto.type", "");
-        return "file".equalsIgnoreCase(status);
+        return RoSystemProperties.CRYPTO_FILE_ENCRYPTED;
     }
 
     /** {@hide}
@@ -1204,8 +1201,7 @@
         if (!isEncrypted()) {
             return false;
         }
-        final String status = SystemProperties.get("ro.crypto.type", "");
-        return "block".equalsIgnoreCase(status);
+        return RoSystemProperties.CRYPTO_BLOCK_ENCRYPTED;
     }
 
     /** {@hide}
diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java
new file mode 100644
index 0000000..80c55fb
--- /dev/null
+++ b/core/java/com/android/internal/os/RoSystemProperties.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.os.SystemProperties;
+
+/**
+ * This is a cache of various ro.* properties so that they can be read just once
+ * at class init time.
+ */
+public class RoSystemProperties {
+    public static final boolean DEBUGGABLE =
+            SystemProperties.getInt("ro.debuggable", 0) == 1;
+    public static final int FACTORYTEST =
+            SystemProperties.getInt("ro.factorytest", 0);
+
+    // ------ ro.config.* -------- //
+    public static final boolean CONFIG_LOW_RAM =
+            SystemProperties.getBoolean("ro.config.low_ram", false);
+
+    // ------ ro.fw.* ------------ //
+    public static final boolean FW_SYSTEM_USER_SPLIT =
+            SystemProperties.getBoolean("ro.fw.system_user_split", false);
+
+    // ------ ro.crypto.* -------- //
+    public static final String CRYPTO_STATE = SystemProperties.get("ro.crypto.state");
+    public static final String CRYPTO_TYPE = SystemProperties.get("ro.crypto.type");
+    // These are pseudo-properties
+    public static final boolean CRYPTO_ENCRYPTABLE =
+            !CRYPTO_STATE.isEmpty() && !"unsupported".equals(CRYPTO_STATE);
+    public static final boolean CRYPTO_ENCRYPTED =
+            "encrypted".equalsIgnoreCase(CRYPTO_STATE);
+    public static final boolean CRYPTO_FILE_ENCRYPTED =
+            "file".equalsIgnoreCase(CRYPTO_TYPE);
+    public static final boolean CRYPTO_BLOCK_ENCRYPTED =
+            "block".equalsIgnoreCase(CRYPTO_TYPE);
+}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index ec80303..763d392 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -24,6 +24,7 @@
 
 import android.net.Credentials;
 import android.net.LocalSocket;
+import android.os.FactoryTest;
 import android.os.Process;
 import android.os.SELinux;
 import android.os.SystemProperties;
@@ -642,13 +643,10 @@
             throws ZygoteSecurityException {
 
         if (peer.getUid() == Process.SYSTEM_UID) {
-            String factoryTest = SystemProperties.get("ro.factorytest");
-            boolean uidRestricted;
-
             /* In normal operation, SYSTEM_UID can only specify a restricted
              * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
              */
-            uidRestricted = !(factoryTest.equals("1") || factoryTest.equals("2"));
+            boolean uidRestricted = FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF;
 
             if (uidRestricted && args.uidSpecified && (args.uid < Process.SYSTEM_UID)) {
                 throw new ZygoteSecurityException(
@@ -678,7 +676,7 @@
      * @param args non-null; zygote spawner args
      */
     public static void applyDebuggerSystemProperty(Arguments args) {
-        if ("1".equals(SystemProperties.get("ro.debuggable"))) {
+        if (RoSystemProperties.DEBUGGABLE) {
             args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
         }
     }
diff --git a/preloaded-classes b/preloaded-classes
index 805a1c9..42f290e 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -2542,6 +2542,7 @@
 com.android.internal.os.RuntimeInit$Arguments
 com.android.internal.os.RuntimeInit$KillApplicationHandler
 com.android.internal.os.RuntimeInit$LoggingHandler
+com.android.internal.os.RoSystemProperties
 com.android.internal.os.SamplingProfilerIntegration
 com.android.internal.os.SomeArgs
 com.android.internal.os.Zygote