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