• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.base;
6 
7 import android.os.Build;
8 import android.os.StrictMode;
9 import android.util.Log;
10 
11 import java.io.BufferedReader;
12 import java.io.FileReader;
13 import java.util.regex.Matcher;
14 import java.util.regex.Pattern;
15 
16 /**
17  * Exposes system related information about the current device.
18  */
19 public class SysUtils {
20     // Any device that runs this or an older version of the system cannot be considered 'low-end'
21     private static final int ANDROID_LOW_MEMORY_ANDROID_SDK_THRESHOLD =
22             Build.VERSION_CODES.JELLY_BEAN_MR2;
23 
24     // A device reporting strictly more total memory in megabytes cannot be considered 'low-end'.
25     private static final long ANDROID_LOW_MEMORY_DEVICE_THRESHOLD_MB = 512;
26 
27     private static final String TAG = "SysUtils";
28 
29     private static Boolean sLowEndDevice;
30 
SysUtils()31     private SysUtils() { }
32 
33     /**
34      * Return the amount of physical memory on this device in kilobytes.
35      * @return Amount of physical memory in kilobytes, or 0 if there was
36      *         an error trying to access the information.
37      */
amountOfPhysicalMemoryKB()38     private static int amountOfPhysicalMemoryKB() {
39         // Extract total memory RAM size by parsing /proc/meminfo, note that
40         // this is exactly what the implementation of sysconf(_SC_PHYS_PAGES)
41         // does. However, it can't be called because this method must be
42         // usable before any native code is loaded.
43 
44         // An alternative is to use ActivityManager.getMemoryInfo(), but this
45         // requires a valid ActivityManager handle, which can only come from
46         // a valid Context object, which itself cannot be retrieved
47         // during early startup, where this method is called. And making it
48         // an explicit parameter here makes all call paths _much_ more
49         // complicated.
50 
51         Pattern pattern = Pattern.compile("^MemTotal:\\s+([0-9]+) kB$");
52         // Synchronously reading files in /proc in the UI thread is safe.
53         StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
54         try {
55             FileReader fileReader = new FileReader("/proc/meminfo");
56             try {
57                 BufferedReader reader = new BufferedReader(fileReader);
58                 try {
59                     String line;
60                     for (;;) {
61                         line = reader.readLine();
62                         if (line == null) {
63                             Log.w(TAG, "/proc/meminfo lacks a MemTotal entry?");
64                             break;
65                         }
66                         Matcher m = pattern.matcher(line);
67                         if (!m.find()) continue;
68 
69                         int totalMemoryKB = Integer.parseInt(m.group(1));
70                         // Sanity check.
71                         if (totalMemoryKB <= 1024) {
72                             Log.w(TAG, "Invalid /proc/meminfo total size in kB: " + m.group(1));
73                             break;
74                         }
75 
76                         return totalMemoryKB;
77                     }
78 
79                 } finally {
80                     reader.close();
81                 }
82             } finally {
83                 fileReader.close();
84             }
85         } catch (Exception e) {
86             Log.w(TAG, "Cannot get total physical size from /proc/meminfo", e);
87         } finally {
88             StrictMode.setThreadPolicy(oldPolicy);
89         }
90 
91         return 0;
92     }
93 
94     /**
95      * @return Whether or not this device should be considered a low end device.
96      */
97     @CalledByNative
isLowEndDevice()98     public static boolean isLowEndDevice() {
99         if (sLowEndDevice == null) {
100             sLowEndDevice = detectLowEndDevice();
101         }
102         return sLowEndDevice.booleanValue();
103     }
104 
detectLowEndDevice()105     private static boolean detectLowEndDevice() {
106         assert CommandLine.isInitialized();
107         if (CommandLine.getInstance().hasSwitch(BaseSwitches.LOW_END_DEVICE_MODE)) {
108             int mode = Integer.parseInt(CommandLine.getInstance().getSwitchValue(
109                     BaseSwitches.LOW_END_DEVICE_MODE));
110             if (mode == 1) return true;
111             if (mode == 0) return false;
112         }
113 
114         if (Build.VERSION.SDK_INT <= ANDROID_LOW_MEMORY_ANDROID_SDK_THRESHOLD) {
115             return false;
116         }
117 
118         int ramSizeKB = amountOfPhysicalMemoryKB();
119         return (ramSizeKB > 0 && ramSizeKB / 1024 < ANDROID_LOW_MEMORY_DEVICE_THRESHOLD_MB);
120     }
121 }
122