1 #region Copyright notice and license
3 // Copyright 2015 gRPC authors.
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
20 using System.Collections.Concurrent;
21 using System.Diagnostics;
24 using System.Reflection;
25 using System.Runtime.InteropServices;
26 using System.Threading;
27 using Grpc.Core.Utils;
29 namespace Grpc.Core.Internal
32 /// Utility methods for detecting platform and architecture.
34 internal static class PlatformApis
36 const string UnityEngineAssemblyName = "UnityEngine";
38 const string UnityEngineApplicationClassName = "UnityEngine.Application";
40 const string UnityIPhonePlayer = "IPhonePlayer";
41 const string XamarinAndroidObjectClassName = "Java.Lang.Object, Mono.Android";
42 const string XamarinIOSObjectClassName = "Foundation.NSObject, Xamarin.iOS";
44 static readonly bool isLinux;
45 static readonly bool isMacOSX;
46 static readonly bool isWindows;
47 static readonly bool isMono;
48 static readonly bool isNet5OrHigher;
49 static readonly bool isNetCore;
50 static readonly string unityApplicationPlatform;
51 static readonly bool isXamarin;
52 static readonly bool isXamarinIOS;
53 static readonly bool isXamarinAndroid;
58 isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
59 isMacOSX = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
60 isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
62 isNet5OrHigher = Environment.Version.Major >= 5;
64 // assume that on .NET 5+, the netstandard2.0 TFM is going to be selected.
65 isNet5OrHigher = false;
67 isNetCore = isNet5OrHigher || RuntimeInformation.FrameworkDescription.StartsWith(".NET Core");
69 var platform = Environment.OSVersion.Platform;
71 // PlatformID.MacOSX is never returned, commonly used trick is to identify Mac is by using uname.
72 isMacOSX = (platform == PlatformID.Unix && GetUname() == "Darwin");
73 isLinux = (platform == PlatformID.Unix && !isMacOSX);
74 isWindows = (platform == PlatformID.Win32NT || platform == PlatformID.Win32S || platform == PlatformID.Win32Windows);
75 isNet5OrHigher = false;
78 isMono = Type.GetType("Mono.Runtime") != null;
81 unityApplicationPlatform = TryGetUnityApplicationPlatform();
84 isXamarinIOS = Type.GetType(XamarinIOSObjectClassName) != null;
85 isXamarinAndroid = Type.GetType(XamarinAndroidObjectClassName) != null;
86 isXamarin = isXamarinIOS || isXamarinAndroid;
89 public static bool IsLinux => isLinux;
91 public static bool IsMacOSX => isMacOSX;
93 public static bool IsWindows => isWindows;
95 public static bool IsMono => isMono;
98 /// true if running on Unity platform.
100 public static bool IsUnity => unityApplicationPlatform != null;
103 /// true if running on Unity iOS, false otherwise.
105 public static bool IsUnityIOS => unityApplicationPlatform == UnityIPhonePlayer;
108 /// true if running on a Xamarin platform (either Xamarin.Android or Xamarin.iOS),
111 public static bool IsXamarin => isXamarin;
114 /// true if running on Xamarin.iOS, false otherwise.
116 public static bool IsXamarinIOS => isXamarinIOS;
119 /// true if running on Xamarin.Android, false otherwise.
121 public static bool IsXamarinAndroid => isXamarinAndroid;
124 /// true if running on .NET 5+, false otherwise.
126 public static bool IsNet5OrHigher => isNet5OrHigher;
129 /// true if running on .NET Core (CoreCLR) or NET 5+, false otherwise.
131 public static bool IsNetCore => isNetCore;
133 public static bool Is64Bit => IntPtr.Size == 8;
136 /// Returns <c>UnityEngine.Application.platform</c> as a string.
137 /// See https://docs.unity3d.com/ScriptReference/Application-platform.html for possible values.
138 /// Value is obtained via reflection to avoid compile-time dependency on Unity.
139 /// This method should only be called if <c>IsUnity</c> is <c>true</c>.
141 public static string GetUnityApplicationPlatform()
143 GrpcPreconditions.CheckState(IsUnity, "Not running on Unity.");
144 return unityApplicationPlatform;
148 /// Returns <c>UnityEngine.Application.platform</c> as a string or <c>null</c>
149 /// if not running on Unity.
150 /// Value is obtained via reflection to avoid compile-time dependency on Unity.
152 static string TryGetUnityApplicationPlatform()
154 Assembly unityAssembly = null;
156 // On netstandard1.5, AppDomain is not available and we just short-circuit the logic there.
157 // This is fine because only the net45 or netstandard2.0 version Grpc.Core assembly is going to used in Unity.
158 // NOTE: Instead of trying to load the UnityEngine.Application class via <c>Type.GetType()</c>
159 // we are using a more sneaky approach to avoid inadvertently loading the UnityEngine
160 // assembly (that might be available even when we are not actually on Unity, resulting
161 // in false positive). See https://github.com/grpc/grpc/issues/18801
162 unityAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(assembly => assembly.GetName().Name == UnityEngineAssemblyName);
164 var applicationClass = unityAssembly?.GetType(UnityEngineApplicationClassName);
165 var platformProperty = applicationClass?.GetTypeInfo().GetProperty("platform", BindingFlags.Static | BindingFlags.Public);
168 // Consult value of Application.platform via reflection
169 // https://docs.unity3d.com/ScriptReference/Application-platform.html
170 return platformProperty?.GetValue(null)?.ToString();
172 catch (TargetInvocationException)
174 // The getter for Application.platform is defined as "extern", so if UnityEngine assembly is loaded outside of a Unity application,
175 // the definition for the getter will be missing - note that this is a sneaky trick that helps us tell a real Unity application from a non-unity
176 // application which just happens to have loaded the UnityEngine.dll assembly.
177 // https://github.com/Unity-Technologies/UnityCsReference/blob/61f92bd79ae862c4465d35270f9d1d57befd1761/Runtime/Export/Application/Application.bindings.cs#L375
178 // See https://github.com/grpc/grpc/issues/23334
180 // If TargetInvocationException was thrown, it most likely means that the method definition for the extern method is missing,
181 // and we are going to interpret this as "not running on Unity".
187 static extern int uname(IntPtr buf);
189 static string GetUname()
191 var buffer = Marshal.AllocHGlobal(8192);
194 if (uname(buffer) == 0)
196 return Marshal.PtrToStringAnsi(buffer);
206 if (buffer != IntPtr.Zero)
208 Marshal.FreeHGlobal(buffer);