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