using Android.OS;
using Android.Runtime;
using Java.Lang;
using Java.Util;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Exception = System.Exception;
using JRuntime = Java.Lang.Runtime;
using String = Java.Lang.String;
// ReSharper disable once CheckNamespace
namespace Android.OS
{
///
/// 通过 Java 反射调用 android.os.SystemProperties
/// https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/SystemProperties.java
///
internal static class SystemProperties
{
[Obsolete]
public static string? Get(string key) => lazy_native_get.Value.Invoke(key);
[Obsolete]
public static string? Get(string key, string? def) => lazy_native_get2.Value.Invoke(key, def);
[Obsolete]
public static int GetInt(string key, int def) => lazy_native_get_int.Value.Invoke(key, def);
[Obsolete]
public static long GetLong(string key, long def) => lazy_native_get_long.Value.Invoke(key, def);
[Obsolete]
public static bool GetBoolean(string key, bool def) => lazy_native_get_boolean.Value.Invoke(key, def);
[Obsolete]
public static void Set(string key, string val) => lazy_native_set.Value.Invoke(key, val);
#region 反射实现
#pragma warning disable IDE1006 // 命名样式
static TResult reflection_call_convert(Func convert, string name, IEnumerable types, params Java.Lang.Object?[] args)
{
var t = reflection_call(name, types, args);
return convert(t);
}
static TResult reflection_call_convert(Func convert, string name, IEnumerable types, params Java.Lang.Object?[] args) where T : class, IJavaObject
{
var t = reflection_call(name, types, args).JavaCast();
return convert(t);
}
static Java.Lang.Object? reflection_call(string name, IEnumerable types, params Java.Lang.Object?[] args)
{
var @class = lazy_native_class.Value;
var parameterTypes = types.Select(x => Class.FromType(x)).ToArray();
var method = @class.GetMethod(name, parameterTypes);
#pragma warning disable CS8620 // 由于引用类型的可为 null 性差异,实参不能用于形参。
var resultObject = method.Invoke(@class, args);
#pragma warning restore CS8620 // 由于引用类型的可为 null 性差异,实参不能用于形参。
return resultObject;
}
static string? convert_string_to_string(String? s) => s?.ToString();
static int convert_object_to_int(Java.Lang.Object? o) => o == default ? default : (int)o;
static long convert_object_to_long(Java.Lang.Object? o) => o == default ? default : (long)o;
static bool convert_object_to_bool(Java.Lang.Object? o) => o != default && (bool)o;
static readonly Lazy lazy_native_class
= new Lazy(() => Class.ForName("android.os.SystemProperties"));
static readonly Lazy> lazy_native_get
= new Lazy>(() => (key)
=> reflection_call_convert(convert_string_to_string, "get", new[] { typeof(String) }, new String(key)));
static readonly Lazy> lazy_native_get2
= new Lazy>(() => (key, def)
=> reflection_call_convert(convert_string_to_string, "get", new[] { typeof(String), typeof(String) }, key.ToJavaString(), def.ToJavaString_Nullable()));
static readonly Lazy> lazy_native_get_int
= new Lazy>(() => (key, def)
=> reflection_call_convert(convert_object_to_int, "getInt", new[] { typeof(String), typeof(int) }, new String(key), def));
static readonly Lazy> lazy_native_get_long
= new Lazy>(() => (key, def)
=> reflection_call_convert(convert_object_to_long, "getLong", new[] { typeof(String), typeof(long) }, new String(key), def));
static readonly Lazy> lazy_native_get_boolean
= new Lazy>(() => (key, def)
=> reflection_call_convert(convert_object_to_bool, "getBoolean", new[] { typeof(String), typeof(bool) }, new String(key), def));
static readonly Lazy> lazy_native_set
= new Lazy>(() => (key, val)
=> reflection_call("set", new[] { typeof(String), typeof(String) }, new String(key), new String(val)));
#pragma warning restore IDE1006 // 命名样式
#endregion
[Conditional("DEBUG")]
static void LogError(this Exception ex, string message)
=> DI.Get().CreateLogger("SystemProperties").LogError(ex, message);
static readonly Lazy mBuildProperties
= new Lazy(GetBuildProperties);
const string planA = "/system/build.prop";
const string planB = "getprop";
public static Properties? GetBuildPropertiesByPlanA()
{
var fileInfo = new FileInfo(planA);
if (fileInfo.Exists)
{
var properties = new Properties();
properties.Load(fileInfo.OpenRead());
return properties;
}
return default;
}
public static Properties? GetBuildPropertiesByPlanB()
{
var runtime = JRuntime.GetRuntime();
if (runtime != null)
{
using var getpropProcess = runtime.Exec(planB);
if (getpropProcess != null)
{
using var streamReader = new Java.IO.InputStreamReader(getpropProcess.InputStream);
using var buildProperties = new Java.IO.BufferedReader(streamReader);
var properties = new Properties();
string? line;
while ((line = buildProperties.ReadLine()) != null)
{
var key_value = line
.Replace("[", string.Empty)
.Replace("]", string.Empty)
.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim()).ToArray();
if (key_value.Length == 2)
{
properties.SetProperty(key_value[0], key_value[1]);
}
}
getpropProcess.WaitFor();
buildProperties.Close();
return properties;
}
}
return default;
}
internal static Properties? TryGetBuildPropertiesByPlanA()
{
try
{
return GetBuildPropertiesByPlanA();
}
catch (Exception ex)
{
LogError(ex, planA);
}
return default;
}
internal static Properties? TryGetBuildPropertiesByPlanB()
{
try
{
return GetBuildPropertiesByPlanB();
}
catch (Exception ex)
{
LogError(ex, planB);
}
return default;
}
static Properties? GetBuildProperties()
{
Properties? properties;
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
{
properties = TryGetBuildPropertiesByPlanA();
if (properties == null) properties = TryGetBuildPropertiesByPlanB();
}
else
{
properties = TryGetBuildPropertiesByPlanB();
if (properties == null) properties = TryGetBuildPropertiesByPlanA();
}
return properties;
}
public static Properties? BuildProperties => mBuildProperties.Value;
public static string Print(this Properties? properties)
{
if (properties != null)
{
const string start = "Count = ";
var keys = properties.StringPropertyNames();
if (keys != null)
{
var sb = new System.Text.StringBuilder(start);
sb.AppendLine(keys.Count.ToString());
foreach (var key in keys)
{
sb.Append(key);
sb.Append(" = ");
sb.AppendLine(properties.GetProperty(key));
}
return sb.ToString();
}
else
{
return start;
}
}
return string.Empty;
}
public static bool TryGet(string key, [NotNullWhen(true)] out string? value)
{
try
{
#pragma warning disable CS0612 // 类型或成员已过时
value = Get(key);
#pragma warning restore CS0612 // 类型或成员已过时
#pragma warning disable CS8762 // 在某些条件下退出时,参数必须具有非 null 值。
return !string.IsNullOrWhiteSpace(value);
#pragma warning restore CS8762 // 在某些条件下退出时,参数必须具有非 null 值。
}
catch (Exception ex1)
{
ex1.LogError("SystemProperties.Get");
try
{
value = BuildProperties?.GetProperty(key);
#pragma warning disable CS8762 // 在某些条件下退出时,参数必须具有非 null 值。
return !string.IsNullOrWhiteSpace(value);
#pragma warning restore CS8762 // 在某些条件下退出时,参数必须具有非 null 值。
}
catch (Exception ex2)
{
ex2.LogError("BuildProperties.GetProperty");
}
}
value = null;
return false;
}
}
}