1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/util/HardwareUtils.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,199 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko.util; 1.10 + 1.11 +import android.content.Context; 1.12 +import android.content.pm.PackageManager; 1.13 +import android.content.res.Configuration; 1.14 +import android.os.Build; 1.15 +import android.util.Log; 1.16 +import android.view.ViewConfiguration; 1.17 + 1.18 +import java.io.FileInputStream; 1.19 +import java.io.FileNotFoundException; 1.20 +import java.io.IOException; 1.21 +import java.util.regex.Matcher; 1.22 +import java.util.regex.Pattern; 1.23 + 1.24 +public final class HardwareUtils { 1.25 + private static final String LOGTAG = "GeckoHardwareUtils"; 1.26 + 1.27 + // Minimum memory threshold for a device to be considered 1.28 + // a low memory platform (see isLowMemoryPlatform). This value 1.29 + // has be in sync with Gecko's equivalent threshold (defined in 1.30 + // xpcom/base/nsMemoryImpl.cpp) and should only be used in cases 1.31 + // where we can't depend on Gecko to be up and running e.g. show/hide 1.32 + // reading list capabilities in HomePager. 1.33 + private static final int LOW_MEMORY_THRESHOLD_MB = 384; 1.34 + 1.35 + // Number of bytes of /proc/meminfo to read in one go. 1.36 + private static final int MEMINFO_BUFFER_SIZE_BYTES = 256; 1.37 + 1.38 + private static volatile int sTotalRAM = -1; 1.39 + 1.40 + private static Context sContext; 1.41 + 1.42 + private static Boolean sIsLargeTablet; 1.43 + private static Boolean sIsSmallTablet; 1.44 + private static Boolean sIsTelevision; 1.45 + private static Boolean sHasMenuButton; 1.46 + 1.47 + private HardwareUtils() { 1.48 + } 1.49 + 1.50 + public static void init(Context context) { 1.51 + if (sContext != null) { 1.52 + Log.w(LOGTAG, "HardwareUtils.init called twice!"); 1.53 + } 1.54 + sContext = context; 1.55 + } 1.56 + 1.57 + public static boolean isTablet() { 1.58 + return isLargeTablet() || isSmallTablet(); 1.59 + } 1.60 + 1.61 + public static boolean isLargeTablet() { 1.62 + if (sIsLargeTablet == null) { 1.63 + int screenLayout = sContext.getResources().getConfiguration().screenLayout; 1.64 + sIsLargeTablet = (Build.VERSION.SDK_INT >= 11 && 1.65 + ((screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE)); 1.66 + } 1.67 + return sIsLargeTablet; 1.68 + } 1.69 + 1.70 + public static boolean isSmallTablet() { 1.71 + if (sIsSmallTablet == null) { 1.72 + int screenLayout = sContext.getResources().getConfiguration().screenLayout; 1.73 + sIsSmallTablet = (Build.VERSION.SDK_INT >= 11 && 1.74 + ((screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE)); 1.75 + } 1.76 + return sIsSmallTablet; 1.77 + } 1.78 + 1.79 + public static boolean isTelevision() { 1.80 + if (sIsTelevision == null) { 1.81 + sIsTelevision = sContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION); 1.82 + } 1.83 + return sIsTelevision; 1.84 + } 1.85 + 1.86 + public static boolean hasMenuButton() { 1.87 + if (sHasMenuButton == null) { 1.88 + sHasMenuButton = Boolean.TRUE; 1.89 + if (Build.VERSION.SDK_INT >= 11) { 1.90 + sHasMenuButton = Boolean.FALSE; 1.91 + } 1.92 + if (Build.VERSION.SDK_INT >= 14) { 1.93 + sHasMenuButton = ViewConfiguration.get(sContext).hasPermanentMenuKey(); 1.94 + } 1.95 + } 1.96 + return sHasMenuButton; 1.97 + } 1.98 + 1.99 + /** 1.100 + * Helper functions used to extract key/value data from /proc/meminfo 1.101 + * Pulled from: 1.102 + * http://androidxref.com/4.2_r1/xref/frameworks/base/core/java/com/android/internal/util/MemInfoReader.java 1.103 + */ 1.104 + 1.105 + private static boolean matchMemText(byte[] buffer, int index, int bufferLength, byte[] text) { 1.106 + final int N = text.length; 1.107 + if ((index + N) >= bufferLength) { 1.108 + return false; 1.109 + } 1.110 + for (int i = 0; i < N; i++) { 1.111 + if (buffer[index + i] != text[i]) { 1.112 + return false; 1.113 + } 1.114 + } 1.115 + return true; 1.116 + } 1.117 + 1.118 + /** 1.119 + * Parses a line like: 1.120 + * 1.121 + * MemTotal: 1605324 kB 1.122 + * 1.123 + * into 1605324. 1.124 + * 1.125 + * @return the first uninterrupted sequence of digits following the 1.126 + * specified index, parsed as an integer value in KB. 1.127 + */ 1.128 + private static int extractMemValue(byte[] buffer, int offset, int length) { 1.129 + if (offset >= length) { 1.130 + return 0; 1.131 + } 1.132 + 1.133 + while (offset < length && buffer[offset] != '\n') { 1.134 + if (buffer[offset] >= '0' && buffer[offset] <= '9') { 1.135 + int start = offset++; 1.136 + while (offset < length && 1.137 + buffer[offset] >= '0' && 1.138 + buffer[offset] <= '9') { 1.139 + ++offset; 1.140 + } 1.141 + return Integer.parseInt(new String(buffer, start, offset - start), 10); 1.142 + } 1.143 + ++offset; 1.144 + } 1.145 + return 0; 1.146 + } 1.147 + 1.148 + /** 1.149 + * Fetch the total memory of the device in MB by parsing /proc/meminfo. 1.150 + * 1.151 + * Of course, Android doesn't have a neat and tidy way to find total 1.152 + * RAM, so we do it by parsing /proc/meminfo. 1.153 + * 1.154 + * @return 0 if a problem occurred, or memory size in MB. 1.155 + */ 1.156 + public static int getMemSize() { 1.157 + if (sTotalRAM >= 0) { 1.158 + return sTotalRAM; 1.159 + } 1.160 + 1.161 + // This is the string "MemTotal" that we're searching for in the buffer. 1.162 + final byte[] MEMTOTAL = {'M', 'e', 'm', 'T', 'o', 't', 'a', 'l'}; 1.163 + try { 1.164 + final byte[] buffer = new byte[MEMINFO_BUFFER_SIZE_BYTES]; 1.165 + final FileInputStream is = new FileInputStream("/proc/meminfo"); 1.166 + try { 1.167 + final int length = is.read(buffer); 1.168 + 1.169 + for (int i = 0; i < length; i++) { 1.170 + if (matchMemText(buffer, i, length, MEMTOTAL)) { 1.171 + i += 8; 1.172 + sTotalRAM = extractMemValue(buffer, i, length) / 1024; 1.173 + Log.d(LOGTAG, "System memory: " + sTotalRAM + "MB."); 1.174 + return sTotalRAM; 1.175 + } 1.176 + } 1.177 + } finally { 1.178 + is.close(); 1.179 + } 1.180 + 1.181 + Log.w(LOGTAG, "Did not find MemTotal line in /proc/meminfo."); 1.182 + return sTotalRAM = 0; 1.183 + } catch (FileNotFoundException f) { 1.184 + return sTotalRAM = 0; 1.185 + } catch (IOException e) { 1.186 + return sTotalRAM = 0; 1.187 + } 1.188 + } 1.189 + 1.190 + public static boolean isLowMemoryPlatform() { 1.191 + final int memSize = getMemSize(); 1.192 + 1.193 + // Fallback to false if we fail to read meminfo 1.194 + // for some reason. 1.195 + if (memSize == 0) { 1.196 + Log.w(LOGTAG, "Could not compute system memory. Falling back to isLowMemoryPlatform = false."); 1.197 + return false; 1.198 + } 1.199 + 1.200 + return memSize < LOW_MEMORY_THRESHOLD_MB; 1.201 + } 1.202 +}