#filter substitution
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.gecko;

import org.mozilla.gecko.util.HardwareUtils;

import android.os.StrictMode;
import android.util.Log;

import java.io.File;
import java.io.FileFilter;

import java.util.regex.Pattern;

/**
 * A collection of system info values, broadly mirroring a subset of
 * nsSystemInfo. See also the constants in AppConstants, which reflect
 * much of nsIXULAppInfo.
 */
public final class SysInfo {
    private static final String LOG_TAG = "GeckoSysInfo";

    // We don't mind an instant of possible duplicate work, we only wish to
    // avoid inconsistency, so we don't bother with synchronization for
    // these.
    private static volatile int cpuCount = -1;

      /**
       * Get the number of cores on the device.
       *
       * We can't use a nice tidy API call, because they're all wrong:
       *
       * <http://stackoverflow.com/questions/7962155/how-can-you-detect-a-dual-core-
       * cpu-on-an-android-device-from-code>
       *
       * This method is based on that code.
       *
       * @return the number of CPU cores, or 1 if the number could not be
       *         determined.
       */
    public static int getCPUCount() {
        if (cpuCount > 0) {
            return cpuCount;
        }

        if (android.os.Build.VERSION.SDK_INT < 9) {
            return readCPUCount();
        }

        // Avoid a strict mode warning... but only on devices that have StrictMode.
        StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
        try {
            return readCPUCount();
        } finally {
            StrictMode.setThreadPolicy(savedPolicy);
        }
    }

    private static int readCPUCount() {
        class CpuFilter implements FileFilter {
            @Override
            public boolean accept(File pathname) {
                return Pattern.matches("cpu[0-9]+", pathname.getName());
            }
        }
        try {
            final File dir = new File("/sys/devices/system/cpu/");
            return cpuCount = dir.listFiles(new CpuFilter()).length;
        } catch (Exception e) {
            Log.w(LOG_TAG, "Assuming 1 CPU; got exception.", e);
            return cpuCount = 1;
        }
    }

    /**
     * Wraps HardwareUtils so callers don't need to know about it.
     */
    public static int getMemSize() {
        return HardwareUtils.getMemSize();
    }

    /**
     * @return the SDK version supported by this device, such as '16'.
     */
    public static int getVersion() {
        return android.os.Build.VERSION.SDK_INT;
    }

    /**
     * @return the release version string, such as "4.1.2".
     */
    public static String getReleaseVersion() {
        return android.os.Build.VERSION.RELEASE;
    }

    /**
     * @return the kernel version string, such as "3.4.10-geb45596".
     */
    public static String getKernelVersion() {
        return System.getProperty("os.version", "");
    }

    /**
     * @return the device manufacturer, such as "HTC".
     */
    public static String getManufacturer() {
        return android.os.Build.MANUFACTURER;
    }

    /**
     * @return the device name, such as "HTC One".
     */
    public static String getDevice() {
        // No, not android.os.Build.DEVICE.
        return android.os.Build.MODEL;
    }

    /**
     * @return the Android "hardware" identifier, such as "m7".
     */
    public static String getHardware() {
        return android.os.Build.HARDWARE;
    }

    /**
     * @return the system OS name. Hardcoded to "Android".
     */
    public static String getName() {
        // We deliberately differ from PR_SI_SYSNAME, which is "Linux".
        return "Android";
    }

    /**
     * @return the architecture string, excluding ABI.
     */
    public static String getArch() {
        return "@CPU_ARCH@";   // "arm"
    }

    /**
     * @return the Android architecture string, including ABI.
     */
    public static String getArchABI() {
        // Android likes to include the ABI, too ("armeabiv7"), so we
        // differ to add value.
        return android.os.Build.CPU_ABI;
    }

    /**
     * @return the default system locale, such as "en-US"
     */
    public static String getLocale() {
        return java.util.Locale.getDefault().toString().replace('_', '-');
    }
}
