Imported Upstream version 1.36.0
[platform/upstream/grpc.git] / src / csharp / Grpc.Core / Internal / PlatformApis.cs
1 #region Copyright notice and license
2
3 // Copyright 2015 gRPC authors.
4 //
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
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
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.
16
17 #endregion
18
19 using System;
20 using System.Collections.Concurrent;
21 using System.Diagnostics;
22 using System.IO;
23 using System.Linq;
24 using System.Reflection;
25 using System.Runtime.InteropServices;
26 using System.Threading;
27 using Grpc.Core.Utils;
28
29 namespace Grpc.Core.Internal
30 {
31     /// <summary>
32     /// Utility methods for detecting platform and architecture.
33     /// </summary>
34     internal static class PlatformApis
35     {
36         const string UnityEngineAssemblyName = "UnityEngine";
37
38         const string UnityEngineApplicationClassName = "UnityEngine.Application";
39
40         const string UnityIPhonePlayer = "IPhonePlayer";
41         const string XamarinAndroidObjectClassName = "Java.Lang.Object, Mono.Android";
42         const string XamarinIOSObjectClassName = "Foundation.NSObject, Xamarin.iOS";
43
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;
54
55         static PlatformApis()
56         {
57 #if NETSTANDARD
58             isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
59             isMacOSX = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
60             isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
61 #if NETSTANDARD2_0
62             isNet5OrHigher = Environment.Version.Major >= 5;
63 #else
64             // assume that on .NET 5+, the netstandard2.0 TFM is going to be selected.
65             isNet5OrHigher = false;
66 #endif
67             isNetCore = isNet5OrHigher || RuntimeInformation.FrameworkDescription.StartsWith(".NET Core");
68 #else
69             var platform = Environment.OSVersion.Platform;
70
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;
76             isNetCore = false;
77 #endif
78             isMono = Type.GetType("Mono.Runtime") != null;
79
80             // Unity
81             unityApplicationPlatform = TryGetUnityApplicationPlatform();
82
83             // Xamarin
84             isXamarinIOS = Type.GetType(XamarinIOSObjectClassName) != null;
85             isXamarinAndroid = Type.GetType(XamarinAndroidObjectClassName) != null;
86             isXamarin = isXamarinIOS || isXamarinAndroid;
87         }
88
89         public static bool IsLinux => isLinux;
90
91         public static bool IsMacOSX => isMacOSX;
92
93         public static bool IsWindows => isWindows;
94
95         public static bool IsMono => isMono;
96
97         /// <summary>
98         /// true if running on Unity platform.
99         /// </summary>
100         public static bool IsUnity => unityApplicationPlatform != null;
101
102         /// <summary>
103         /// true if running on Unity iOS, false otherwise.
104         /// </summary>
105         public static bool IsUnityIOS => unityApplicationPlatform == UnityIPhonePlayer;
106
107         /// <summary>
108         /// true if running on a Xamarin platform (either Xamarin.Android or Xamarin.iOS),
109         /// false otherwise.
110         /// </summary>
111         public static bool IsXamarin => isXamarin;
112
113         /// <summary>
114         /// true if running on Xamarin.iOS, false otherwise.
115         /// </summary>
116         public static bool IsXamarinIOS => isXamarinIOS;
117
118         /// <summary>
119         /// true if running on Xamarin.Android, false otherwise.
120         /// </summary>
121         public static bool IsXamarinAndroid => isXamarinAndroid;
122
123         /// <summary>
124         /// true if running on .NET 5+, false otherwise.
125         /// </summary>
126         public static bool IsNet5OrHigher => isNet5OrHigher;
127
128         /// <summary>
129         /// true if running on .NET Core (CoreCLR) or NET 5+, false otherwise.
130         /// </summary>
131         public static bool IsNetCore => isNetCore;
132
133         public static bool Is64Bit => IntPtr.Size == 8;
134
135         /// <summary>
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>.
140         /// </summary>
141         public static string GetUnityApplicationPlatform()
142         {
143             GrpcPreconditions.CheckState(IsUnity, "Not running on Unity.");
144             return unityApplicationPlatform;
145         }
146
147         /// <summary>
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.
151         /// </summary>
152         static string TryGetUnityApplicationPlatform()
153         {
154             Assembly unityAssembly = null;
155 #if !NETSTANDARD1_5
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);
163 #endif
164             var applicationClass = unityAssembly?.GetType(UnityEngineApplicationClassName);
165             var platformProperty = applicationClass?.GetTypeInfo().GetProperty("platform", BindingFlags.Static | BindingFlags.Public);
166             try
167             {
168                 // Consult value of Application.platform via reflection
169                 // https://docs.unity3d.com/ScriptReference/Application-platform.html
170                 return platformProperty?.GetValue(null)?.ToString();
171             }
172             catch (TargetInvocationException)
173             {
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
179
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".
182                 return null;
183             }
184         }
185
186         [DllImport("libc")]
187         static extern int uname(IntPtr buf);
188
189         static string GetUname()
190         {
191             var buffer = Marshal.AllocHGlobal(8192);
192             try
193             {
194                 if (uname(buffer) == 0)
195                 {
196                     return Marshal.PtrToStringAnsi(buffer);
197                 }
198                 return string.Empty;
199             }
200             catch
201             {
202                 return string.Empty;
203             }
204             finally
205             {
206                 if (buffer != IntPtr.Zero)
207                 {
208                     Marshal.FreeHGlobal(buffer);
209                 }
210             }
211         }
212     }
213 }