io.github.apkcloud.devicedetector.parser.OperatingSystem Maven / Gradle / Ivy
Show all versions of DeviceDetector Show documentation
package io.github.apkcloud.devicedetector.parser;
import io.github.apkcloud.devicedetector.entity.OS;
import java.util.*;
/**
* 解析 UserAgent 以获取操作系统信息。
*
* 检测到的操作系统可以在 {@link #operatingSystems} 和 resources/regexes/oss.yml 中找到。
*
* 此类还定义了一些操作系统家族和获取具体操作系统家族的方法。
*/
public class OperatingSystem extends AbstractParser {
protected String fixtureFile = "regexes/oss.yml";
protected String parserName = "os";
/**
* 已知的操作系统映射到其内部短代码
*/
protected static Map operatingSystems = new HashMap() {
{
put("AIX", "AIX");
put("AND", "Android");
put("ADR", "Android TV");
put("AMZ", "Amazon Linux");
put("AMG", "AmigaOS");
put("ATV", "tvOS");
put("ARL", "Arch Linux");
put("BTR", "BackTrack");
put("SBA", "Bada");
put("BEO", "BeOS");
put("BLB", "BlackBerry OS");
put("QNX", "BlackBerry Tablet OS");
put("BOS", "Bliss OS");
put("BMP", "Brew");
put("CAI", "Caixa Mágica");
put("CES", "CentOS");
put("CST", "CentOS Stream");
put("CLR", "ClearOS Mobile");
put("COS", "Chrome OS");
put("CRS", "Chromium OS");
put("CHN", "China OS");
put("CYN", "CyanogenMod");
put("DEB", "Debian");
put("DEE", "Deepin");
put("DFB", "DragonFly");
put("DVK", "DVKBuntu");
put("FED", "Fedora");
put("FEN", "Fenix");
put("FOS", "Firefox OS");
put("FIR", "Fire OS");
put("FOR", "Foresight Linux");
put("FRE", "Freebox");
put("BSD", "FreeBSD");
put("FYD", "FydeOS");
put("FUC", "Fuchsia");
put("GNT", "Gentoo");
put("GRI", "GridOS");
put("GTV", "Google TV");
put("HPX", "HP-UX");
put("HAI", "Haiku OS");
put("IPA", "iPadOS");
put("HAR", "HarmonyOS");
put("HAS", "HasCodingOS");
put("IRI", "IRIX");
put("INF", "Inferno");
put("JME", "Java ME");
put("KOS", "KaiOS");
put("KAN", "Kanotix");
put("KNO", "Knoppix");
put("KTV", "KreaTV");
put("KBT", "Kubuntu");
put("LIN", "GNU/Linux");
put("LND", "LindowsOS");
put("LNS", "Linspire");
put("LEN", "Lineage OS");
put("LBT", "Lubuntu");
put("LOS", "Lumin OS");
put("VLN", "VectorLinux");
put("MAC", "Mac");
put("MAE", "Maemo");
put("MAG", "Mageia");
put("MDR", "Mandriva");
put("SMG", "MeeGo");
put("MCD", "MocorDroid");
put("MON", "moonOS");
put("MIN", "Mint");
put("MLD", "MildWild");
put("MOR", "MorphOS");
put("NBS", "NetBSD");
put("MTK", "MTK / Nucleus");
put("MRE", "MRE");
put("WII", "Nintendo");
put("NDS", "Nintendo Mobile");
put("NOV", "Nova");
put("OS2", "OS/2");
put("T64", "OSF1");
put("OBS", "OpenBSD");
put("OWR", "OpenWrt");
put("OTV", "Opera TV");
put("ORD", "Ordissimo");
put("PAR", "Pardus");
put("PCL", "PCLinuxOS");
put("PLA", "Plasma Mobile");
put("PSP", "PlayStation Portable");
put("PS3", "PlayStation");
put("PUR", "PureOS");
put("RHT", "Red Hat");
put("RED", "RedOS");
put("REV", "Revenge OS");
put("ROS", "RISC OS");
put("ROK", "Roku OS");
put("RSO", "Rosa");
put("ROU", "RouterOS");
put("REM", "Remix OS");
put("RRS", "Resurrection Remix OS");
put("REX", "REX");
put("RZD", "RazoDroiD");
put("SAB", "Sabayon");
put("SSE", "SUSE");
put("SAF", "Sailfish OS");
put("SEE", "SeewoOS");
put("SIR", "Sirin OS");
put("SLW", "Slackware");
put("SOS", "Solaris");
put("SYL", "Syllable");
put("SYM", "Symbian");
put("SYS", "Symbian OS");
put("S40", "Symbian OS Series 40");
put("S60", "Symbian OS Series 60");
put("SY3", "Symbian^3");
put("TEN", "TencentOS");
put("TDX", "ThreadX");
put("TIZ", "Tizen");
put("TOS", "TmaxOS");
put("UBT", "Ubuntu");
put("WAS", "watchOS");
put("WTV", "WebTV");
put("WHS", "Whale OS");
put("WIN", "Windows");
put("WCE", "Windows CE");
put("WIO", "Windows IoT");
put("WMO", "Windows Mobile");
put("WPH", "Windows Phone");
put("WRT", "Windows RT");
put("XBX", "Xbox");
put("XBT", "Xubuntu");
put("YNS", "YunOS");
put("ZEN", "Zenwalk");
put("ZOR", "ZorinOS");
put("IOS", "iOS");
put("POS", "palmOS");
put("WOS", "webOS");
}
};
/**
* 操作系统家族映射到相关操作系统的短代码
*/
protected static Map> osFamilies = new HashMap<>() {
{
put("Android", Arrays.asList("AND", "CYN", "FIR", "REM", "RZD", "MLD", "MCD", "YNS", "GRI", "HAR", "ADR", "CLR", "BOS", "REV", "LEN", "SIR", "RRS"));
put("AmigaOS", Arrays.asList("AMG", "MOR"));
put("BlackBerry", Arrays.asList("BLB", "QNX"));
put("Brew", Arrays.asList("BMP"));
put("BeOS", Arrays.asList("BEO", "HAI"));
put("Chrome OS", Arrays.asList("COS", "CRS", "FYD", "SEE"));
put("Firefox OS", Arrays.asList("FOS", "KOS"));
put("Gaming Console", Arrays.asList("WII", "PS3"));
put("Google TV", Arrays.asList("GTV"));
put("IBM", Arrays.asList("OS2"));
put("iOS", Arrays.asList("IOS", "ATV", "WAS", "IPA"));
put("RISC OS", Arrays.asList("ROS"));
put("GNU/Linux", Arrays.asList(
"LIN", "ARL", "DEB", "KNO", "MIN", "UBT", "KBT", "XBT", "LBT", "FED",
"RHT", "VLN", "MDR", "GNT", "SAB", "SLW", "SSE", "CES", "BTR", "SAF",
"ORD", "TOS", "RSO", "DEE", "FRE", "MAG", "FEN", "CAI", "PCL", "HAS",
"LOS", "DVK", "ROK", "OWR", "OTV", "KTV", "PUR", "PLA", "FUC", "PAR",
"FOR", "MON", "KAN", "ZEN", "LND", "LNS", "CHN", "AMZ", "TEN", "CST",
"NOV", "ROU", "ZOR", "RED"
)
);
put("Mac", Arrays.asList("MAC"));
put("Mobile Gaming Console", Arrays.asList("PSP", "NDS", "XBX"));
put("Real-time OS", Arrays.asList("MTK", "TDX", "MRE", "JME", "REX"));
put("Other Mobile", Arrays.asList("WOS", "POS", "SBA", "TIZ", "SMG", "MAE"));
put("Symbian", Arrays.asList("SYM", "SYS", "SY3", "S60", "S40"));
put("Unix", Arrays.asList("SOS", "AIX", "HPX", "BSD", "NBS", "OBS", "DFB", "SYL", "IRI", "T64", "INF"));
put("WebTV", Arrays.asList("WTV"));
put("Windows", Arrays.asList("WIN"));
put("Windows Mobile", Arrays.asList("WPH", "WMO", "WCE", "WRT", "WIO"));
put("Other Smart TV", Arrays.asList("WHS"));
}
};
/**
* 包含从我们使用的操作系统名称到已知 ClientHints 值的映射列表
*/
protected static Map> clientHintMapping = new HashMap<>() {
{
put("GNU/Linux", Arrays.asList("Linux"));
put("Mac", Arrays.asList("MacOS"));
}
};
/**
* 已知仅适用于桌面的操作系统家族
*/
protected static List desktopOsArray = Arrays.asList("AmigaOS", "IBM", "GNU/Linux", "Mac", "Unix", "Windows", "BeOS", "Chrome OS", "Chromium OS");
/**
* 返回所有可用的操作系统
*/
public static Map getAvailableOperatingSystems() {
return operatingSystems;
}
/**
* 返回所有可用的操作系统家族
*/
public static Map> getAvailableOperatingSystemFamilies() {
return osFamilies;
}
/**
* 返回操作系统的名称和短代码
*
* @param name 操作系统的名称
* @return {@code Map}
*/
public static Map getShortOsData(String name) {
String shortName = "UNK";
for (Map.Entry os : operatingSystems.entrySet()) {
if (!os.getValue().equalsIgnoreCase(name)) {
continue;
}
name = os.getValue();
shortName = os.getKey();
break;
}
return Map.of("short", shortName, "name", name);
}
/**
* {@inheritDoc}
*/
public Map parse() throws Exception {
Map osFromClientHints = parseOsFromClientHints();
Map osFromUserAgent = parseOsFromUserAgent();
String name;
String version;
String shortName;
if (!isNullOrEmpty(osFromClientHints.get("name"))) {
name = osFromClientHints.get("name");
version = osFromClientHints.get("version");
// 如果 ClientHints 中未提供,但 UserAgent 中的操作系统家族匹配,则使用 UserAgent 中的 version
if (isNullOrEmpty(version) && Objects.equals(getOsFamily(name), getOsFamily(osFromUserAgent.get("name")))) {
version = osFromUserAgent.get("version");
}
// 如果从 ClientHints 检测到的操作系统名称与 UserAgent 中的操作系统家族匹配
// 但操作系统名称不同,我们使用 UserAgent 中的一个,因为它可能更详细
if (Objects.equals(getOsFamily(osFromUserAgent.get("name")), name) && !(osFromUserAgent.get("name").equals(name))) {
name = osFromUserAgent.get("name");
if ("HarmonyOS".equals(name)) {
version = "";
}
}
shortName = osFromClientHints.get("short_name");
// Chrome OS 在某些情况下被报告为 Linux,只有当版本号匹配时我们才修复此问题
if ("GNU/Linux".equals(name)
&& "Chrome OS".equals(osFromUserAgent.get("name"))
&& osFromClientHints.get("version").equals(osFromUserAgent.get("version"))) {
name = osFromUserAgent.get("name");
shortName = osFromUserAgent.get("short_name");
}
} else if (!isNullOrEmpty(osFromUserAgent.get("name"))) {
name = osFromUserAgent.get("name");
version = osFromUserAgent.get("version");
shortName = osFromUserAgent.get("short_name");
} else {
return new HashMap<>();
}
String platform = parsePlatform();
String family = getOsFamily(shortName);
String[] androidApps = new String[]{"com.hisense.odinbrowser", "com.seraphic.openinet.pre", "com.appssppa.idesktoppcbrowser"};
if (clientHints != null) {
if (Arrays.asList(androidApps).contains(clientHints.getApp()) && !"Android".equals(name)) {
name = "Android";
family = "Android";
shortName = "ADR";
version = "";
}
}
Map osInfo = new HashMap<>();
osInfo.put("name", name);
osInfo.put("short_name", shortName);
osInfo.put("version", version);
osInfo.put("platform", platform);
osInfo.put("family", family);
if (operatingSystems.containsValue(osInfo.get("name"))) {
osInfo.put("short_name", getKeyByValue(operatingSystems, osInfo.get("name")));
}
return osInfo;
}
@Override
public String getName() {
return parserName;
}
/**
* 返回指定操作系统的操作系统家族
*
* @param osLabel 名称或短代码
* @return String|null If null, "Unknown"
*/
public static String getOsFamily(String osLabel) {
if (operatingSystems.containsValue(osLabel)) {
osLabel = getKeyByValue(operatingSystems, osLabel);
}
for (Map.Entry> entry : osFamilies.entrySet()) {
List list = entry.getValue();
if (list.contains(osLabel)) {
return entry.getKey();
}
}
return null;
}
/**
* 如果操作系统是桌面,则返回 true
*
* @param osName 操作系统短代码
* @return boolean
*/
public static boolean isDesktopOs(String osName) {
String osFamily = getOsFamily(osName);
return desktopOsArray.contains(osFamily);
}
/**
* 返回指定简称的全名
*
* @param os
* @param ver
* @return String | null
*/
public static String getNameFromId(String os, String ver) {
if (operatingSystems.containsKey(os)) {
String osFullName = operatingSystems.get(os);
return (osFullName + " " + ver).trim();
}
return null;
}
/**
* 返回从 ClientHints 中安全检测到的操作系统
*
* @return {@code Map}
*/
protected Map parseOsFromClientHints() {
String name = "";
String version = "";
String shortName = "";
if (clientHints != null && !isNullOrEmpty(clientHints.getOperatingSystem())) {
String hintName = applyClientHintMapping(clientHints.getOperatingSystem(), clientHintMapping);
for (Map.Entry entry : operatingSystems.entrySet()) {
if (fuzzyCompare(hintName, entry.getValue())) {
name = entry.getValue();
shortName = entry.getKey();
break;
}
}
version = clientHints.getOperatingSystemVersion();
if ("Windows".equals(name)) {
int majorVersion = Integer.parseInt(version.split("\\.", 2).length > 0 ? version.split("\\.", 1)[0] : "0");
if (majorVersion > 0 && majorVersion < 11) {
version = "10";
} else if (majorVersion > 10) {
version = "11";
}
}
if (Integer.parseInt(version) == 0) {
version = "";
}
}
Map osInfo = new HashMap<>();
osInfo.put("name", name);
osInfo.put("short_name", shortName);
osInfo.put("version", buildVersion(version, new ArrayList<>()));
return osInfo;
}
/**
* 返回从 UserAgent 检测到的操作系统
*
* @return {@code Map}
* @throws Exception
*/
protected Map parseOsFromUserAgent() throws Exception {
OS osRegex = null;
List matcher = null;
String name = "", version = "", shortName = "";
List regexes = getRegexes(fixtureFile, OS.class);
for (OS regex : regexes) {
matcher = matchUserAgent(regex.getRegex());
osRegex = regex;
if (!isNullOrEmpty(matcher)) {
break;
}
}
if (!isNullOrEmpty(matcher)) {
name = buildByMatch(osRegex.getName(), matcher);
Map shortOsData = getShortOsData(name);
name = shortOsData.get("name");
shortName = shortOsData.get("short");
version = osRegex.getVersion() != null ? buildVersion(osRegex.getVersion(), matcher) : "";
List