View Javadoc
1   /*
2    * Copyright 2013 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.util.internal;
17  
18  import io.netty.util.internal.logging.InternalLogger;
19  import io.netty.util.internal.logging.InternalLoggerFactory;
20  import sun.misc.Unsafe;
21  
22  import java.lang.reflect.Constructor;
23  import java.lang.reflect.Field;
24  import java.lang.reflect.InvocationTargetException;
25  import java.lang.reflect.Method;
26  import java.nio.Buffer;
27  import java.nio.ByteBuffer;
28  import java.security.AccessController;
29  import java.security.PrivilegedAction;
30  
31  import static io.netty.util.internal.ObjectUtil.checkNotNull;
32  
33  /**
34   * The {@link PlatformDependent} operations which requires access to {@code sun.misc.*}.
35   */
36  final class PlatformDependent0 {
37  
38      private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent0.class);
39      private static final long ADDRESS_FIELD_OFFSET;
40      private static final long BYTE_ARRAY_BASE_OFFSET;
41      private static final Constructor<?> DIRECT_BUFFER_CONSTRUCTOR;
42      private static final boolean IS_EXPLICIT_NO_UNSAFE = explicitNoUnsafe0();
43      private static final Method ALLOCATE_ARRAY_METHOD;
44      private static final int JAVA_VERSION = javaVersion0();
45      private static final boolean IS_ANDROID = isAndroid0();
46  
47      private static final Throwable UNSAFE_UNAVAILABILITY_CAUSE;
48      private static final Object INTERNAL_UNSAFE;
49      private static final boolean IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE = explicitTryReflectionSetAccessible0();
50  
51      static final Unsafe UNSAFE;
52  
53      /**
54       * Limits the number of bytes to copy per {@link Unsafe#copyMemory(long, long, long)} to allow safepoint polling
55       * during a large copy.
56       */
57      private static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L;
58  
59      private static final boolean UNALIGNED;
60  
61      static {
62          final ByteBuffer direct;
63          Field addressField = null;
64          Method allocateArrayMethod = null;
65          Throwable unsafeUnavailabilityCause = null;
66          Unsafe unsafe;
67          Object internalUnsafe = null;
68  
69          if (isExplicitNoUnsafe()) {
70              direct = null;
71              addressField = null;
72              unsafeUnavailabilityCause = new UnsupportedOperationException("Unsafe explicitly disabled");
73              unsafe = null;
74              internalUnsafe = null;
75          } else {
76              direct = ByteBuffer.allocateDirect(1);
77  
78              // attempt to access field Unsafe#theUnsafe
79              final Object maybeUnsafe = AccessController.doPrivileged(new PrivilegedAction<Object>() {
80                  @Override
81                  public Object run() {
82                      try {
83                          final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
84                          // We always want to try using Unsafe as the access still works on java9 as well and
85                          // we need it for out native-transports and many optimizations.
86                          Throwable cause = ReflectionUtil.trySetAccessible(unsafeField, false);
87                          if (cause != null) {
88                              return cause;
89                          }
90                          // the unsafe instance
91                          return unsafeField.get(null);
92                      } catch (NoSuchFieldException e) {
93                          return e;
94                      } catch (SecurityException e) {
95                          return e;
96                      } catch (IllegalAccessException e) {
97                          return e;
98                      } catch (NoClassDefFoundError e) {
99                          // Also catch NoClassDefFoundError in case someone uses for example OSGI and it made
100                         // Unsafe unloadable.
101                         return e;
102                     }
103                 }
104             });
105 
106             // the conditional check here can not be replaced with checking that maybeUnsafe
107             // is an instanceof Unsafe and reversing the if and else blocks; this is because an
108             // instanceof check against Unsafe will trigger a class load and we might not have
109             // the runtime permission accessClassInPackage.sun.misc
110             if (maybeUnsafe instanceof Throwable) {
111                 unsafe = null;
112                 unsafeUnavailabilityCause = (Throwable) maybeUnsafe;
113                 logger.debug("sun.misc.Unsafe.theUnsafe: unavailable", (Throwable) maybeUnsafe);
114             } else {
115                 unsafe = (Unsafe) maybeUnsafe;
116                 logger.debug("sun.misc.Unsafe.theUnsafe: available");
117             }
118 
119             // ensure the unsafe supports all necessary methods to work around the mistake in the latest OpenJDK
120             // https://github.com/netty/netty/issues/1061
121             // http://www.mail-archive.com/[email protected]/msg00698.html
122             if (unsafe != null) {
123                 final Unsafe finalUnsafe = unsafe;
124                 final Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
125                     @Override
126                     public Object run() {
127                         try {
128                             finalUnsafe.getClass().getDeclaredMethod(
129                                     "copyMemory", Object.class, long.class, Object.class, long.class, long.class);
130                             return null;
131                         } catch (NoSuchMethodException e) {
132                             return e;
133                         } catch (SecurityException e) {
134                             return e;
135                         }
136                     }
137                 });
138 
139                 if (maybeException == null) {
140                     logger.debug("sun.misc.Unsafe.copyMemory: available");
141                 } else {
142                     // Unsafe.copyMemory(Object, long, Object, long, long) unavailable.
143                     unsafe = null;
144                     unsafeUnavailabilityCause = (Throwable) maybeException;
145                     logger.debug("sun.misc.Unsafe.copyMemory: unavailable", (Throwable) maybeException);
146                 }
147             }
148 
149             if (unsafe != null) {
150                 final Unsafe finalUnsafe = unsafe;
151 
152                 // attempt to access field Buffer#address
153                 final Object maybeAddressField = AccessController.doPrivileged(new PrivilegedAction<Object>() {
154                     @Override
155                     public Object run() {
156                         try {
157                             final Field field = Buffer.class.getDeclaredField("address");
158                             // Use Unsafe to read value of the address field. This way it will not fail on JDK9+ which
159                             // will forbid changing the access level via reflection.
160                             final long offset = finalUnsafe.objectFieldOffset(field);
161                             final long address = finalUnsafe.getLong(direct, offset);
162 
163                             // if direct really is a direct buffer, address will be non-zero
164                             if (address == 0) {
165                                 return null;
166                             }
167                             return field;
168                         } catch (NoSuchFieldException e) {
169                             return e;
170                         } catch (SecurityException e) {
171                             return e;
172                         }
173                     }
174                 });
175 
176                 if (maybeAddressField instanceof Field) {
177                     addressField = (Field) maybeAddressField;
178                     logger.debug("java.nio.Buffer.address: available");
179                 } else {
180                     unsafeUnavailabilityCause = (Throwable) maybeAddressField;
181                     logger.debug("java.nio.Buffer.address: unavailable", (Throwable) maybeAddressField);
182 
183                     // If we cannot access the address of a direct buffer, there's no point of using unsafe.
184                     // Let's just pretend unsafe is unavailable for overall simplicity.
185                     unsafe = null;
186                 }
187             }
188 
189             if (unsafe != null) {
190                 // There are assumptions made where ever BYTE_ARRAY_BASE_OFFSET is used (equals, hashCodeAscii, and
191                 // primitive accessors) that arrayIndexScale == 1, and results are undefined if this is not the case.
192                 long byteArrayIndexScale = unsafe.arrayIndexScale(byte[].class);
193                 if (byteArrayIndexScale != 1) {
194                     logger.debug("unsafe.arrayIndexScale is {} (expected: 1). Not using unsafe.", byteArrayIndexScale);
195                     unsafeUnavailabilityCause = new UnsupportedOperationException("Unexpected unsafe.arrayIndexScale");
196                     unsafe = null;
197                 }
198             }
199         }
200         UNSAFE_UNAVAILABILITY_CAUSE = unsafeUnavailabilityCause;
201         UNSAFE = unsafe;
202 
203         if (unsafe == null) {
204             BYTE_ARRAY_BASE_OFFSET = -1;
205             ADDRESS_FIELD_OFFSET = -1;
206             UNALIGNED = false;
207             DIRECT_BUFFER_CONSTRUCTOR = null;
208             ALLOCATE_ARRAY_METHOD = null;
209         } else {
210             Constructor<?> directBufferConstructor;
211             long address = -1;
212             try {
213                 final Object maybeDirectBufferConstructor =
214                         AccessController.doPrivileged(new PrivilegedAction<Object>() {
215                             @Override
216                             public Object run() {
217                                 try {
218                                     final Constructor<?> constructor =
219                                             direct.getClass().getDeclaredConstructor(long.class, int.class);
220                                     Throwable cause = ReflectionUtil.trySetAccessible(constructor, true);
221                                     if (cause != null) {
222                                         return cause;
223                                     }
224                                     return constructor;
225                                 } catch (NoSuchMethodException e) {
226                                     return e;
227                                 } catch (SecurityException e) {
228                                     return e;
229                                 }
230                             }
231                         });
232 
233                 if (maybeDirectBufferConstructor instanceof Constructor<?>) {
234                     address = UNSAFE.allocateMemory(1);
235                     // try to use the constructor now
236                     try {
237                         ((Constructor<?>) maybeDirectBufferConstructor).newInstance(address, 1);
238                         directBufferConstructor = (Constructor<?>) maybeDirectBufferConstructor;
239                         logger.debug("direct buffer constructor: available");
240                     } catch (InstantiationException e) {
241                         directBufferConstructor = null;
242                     } catch (IllegalAccessException e) {
243                         directBufferConstructor = null;
244                     } catch (InvocationTargetException e) {
245                         directBufferConstructor = null;
246                     }
247                 } else {
248                     logger.debug(
249                             "direct buffer constructor: unavailable",
250                             (Throwable) maybeDirectBufferConstructor);
251                     directBufferConstructor = null;
252                 }
253             } finally {
254                 if (address != -1) {
255                     UNSAFE.freeMemory(address);
256                 }
257             }
258             DIRECT_BUFFER_CONSTRUCTOR = directBufferConstructor;
259             ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField);
260             boolean unaligned;
261             Object maybeUnaligned = AccessController.doPrivileged(new PrivilegedAction<Object>() {
262                 @Override
263                 public Object run() {
264                     try {
265                         Class<?> bitsClass =
266                                 Class.forName("java.nio.Bits", false, getSystemClassLoader());
267                         Method unalignedMethod = bitsClass.getDeclaredMethod("unaligned");
268                         Throwable cause = ReflectionUtil.trySetAccessible(unalignedMethod, true);
269                         if (cause != null) {
270                             return cause;
271                         }
272                         return unalignedMethod.invoke(null);
273                     } catch (NoSuchMethodException e) {
274                         return e;
275                     } catch (SecurityException e) {
276                         return e;
277                     } catch (IllegalAccessException e) {
278                         return e;
279                     } catch (ClassNotFoundException e) {
280                         return e;
281                     } catch (InvocationTargetException e) {
282                         return e;
283                     }
284                 }
285             });
286 
287             if (maybeUnaligned instanceof Boolean) {
288                 unaligned = (Boolean) maybeUnaligned;
289                 logger.debug("java.nio.Bits.unaligned: available, {}", unaligned);
290             } else {
291                 String arch = SystemPropertyUtil.get("os.arch", "");
292                 //noinspection DynamicRegexReplaceableByCompiledPattern
293                 unaligned = arch.matches("^(i[3-6]86|x86(_64)?|x64|amd64)$");
294                 Throwable t = (Throwable) maybeUnaligned;
295                 logger.debug("java.nio.Bits.unaligned: unavailable {}", unaligned, t);
296             }
297 
298             UNALIGNED = unaligned;
299             BYTE_ARRAY_BASE_OFFSET = arrayBaseOffset();
300 
301             if (javaVersion() >= 9) {
302                 Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
303                     @Override
304                     public Object run() {
305                         try {
306                             // Java9 has jdk.internal.misc.Unsafe and not all methods are propagated to
307                             // sun.misc.Unsafe
308                             Class<?> internalUnsafeClass = getClassLoader(PlatformDependent0.class)
309                                     .loadClass("jdk.internal.misc.Unsafe");
310                             Method method = internalUnsafeClass.getDeclaredMethod("getUnsafe");
311                             return method.invoke(null);
312                         } catch (Throwable e) {
313                             return e;
314                         }
315                     }
316                 });
317                 if (!(maybeException instanceof Throwable)) {
318                     internalUnsafe = maybeException;
319                     final Object finalInternalUnsafe = internalUnsafe;
320                     maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
321                         @Override
322                         public Object run() {
323                             try {
324                                 return finalInternalUnsafe.getClass().getDeclaredMethod(
325                                         "allocateUninitializedArray", Class.class, int.class);
326                             } catch (NoSuchMethodException e) {
327                                 return e;
328                             } catch (SecurityException e) {
329                                 return e;
330                             }
331                         }
332                     });
333 
334                     if (maybeException instanceof Method) {
335                         try {
336                             Method m = (Method) maybeException;
337                             byte[] bytes = (byte[]) m.invoke(finalInternalUnsafe, byte.class, 8);
338                             assert bytes.length == 8;
339                             allocateArrayMethod = m;
340                         } catch (IllegalAccessException e) {
341                             maybeException = e;
342                         } catch (InvocationTargetException e) {
343                             maybeException = e;
344                         }
345                     }
346                 }
347 
348                 if (maybeException instanceof Throwable) {
349                     logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable",
350                             (Throwable) maybeException);
351                 } else {
352                     logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): available");
353                 }
354             } else {
355                 logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable prior to Java9");
356             }
357             ALLOCATE_ARRAY_METHOD = allocateArrayMethod;
358         }
359 
360         INTERNAL_UNSAFE = internalUnsafe;
361 
362         logger.debug("java.nio.DirectByteBuffer.<init>(long, int): {}",
363                 DIRECT_BUFFER_CONSTRUCTOR != null ? "available" : "unavailable");
364     }
365 
366     static boolean isExplicitNoUnsafe() {
367         return IS_EXPLICIT_NO_UNSAFE;
368     }
369 
370     private static boolean explicitNoUnsafe0() {
371         final boolean noUnsafe = SystemPropertyUtil.getBoolean("io.netty.noUnsafe", false);
372         logger.debug("-Dio.netty.noUnsafe: {}", noUnsafe);
373 
374         if (noUnsafe) {
375             logger.debug("sun.misc.Unsafe: unavailable (io.netty.noUnsafe)");
376             return true;
377         }
378 
379         // Legacy properties
380         boolean tryUnsafe;
381         if (SystemPropertyUtil.contains("io.netty.tryUnsafe")) {
382             tryUnsafe = SystemPropertyUtil.getBoolean("io.netty.tryUnsafe", true);
383         } else {
384             tryUnsafe = SystemPropertyUtil.getBoolean("org.jboss.netty.tryUnsafe", true);
385         }
386 
387         if (!tryUnsafe) {
388             logger.debug("sun.misc.Unsafe: unavailable (io.netty.tryUnsafe/org.jboss.netty.tryUnsafe)");
389             return true;
390         }
391 
392         return false;
393     }
394 
395     static boolean isUnaligned() {
396         return UNALIGNED;
397     }
398 
399     static boolean hasUnsafe() {
400         return UNSAFE != null;
401     }
402 
403     static Throwable getUnsafeUnavailabilityCause() {
404         return UNSAFE_UNAVAILABILITY_CAUSE;
405     }
406 
407     static void throwException(Throwable cause) {
408         // JVM has been observed to crash when passing a null argument. See https://github.com/netty/netty/issues/4131.
409         UNSAFE.throwException(checkNotNull(cause, "cause"));
410     }
411 
412     static boolean hasDirectBufferNoCleanerConstructor() {
413         return DIRECT_BUFFER_CONSTRUCTOR != null;
414     }
415 
416     static ByteBuffer reallocateDirectNoCleaner(ByteBuffer buffer, int capacity) {
417         return newDirectBuffer(UNSAFE.reallocateMemory(directBufferAddress(buffer), capacity), capacity);
418     }
419 
420     static ByteBuffer allocateDirectNoCleaner(int capacity) {
421         return newDirectBuffer(UNSAFE.allocateMemory(capacity), capacity);
422     }
423 
424     static boolean hasAllocateArrayMethod() {
425         return ALLOCATE_ARRAY_METHOD != null;
426     }
427 
428     static byte[] allocateUninitializedArray(int size) {
429         try {
430             return (byte[]) ALLOCATE_ARRAY_METHOD.invoke(INTERNAL_UNSAFE, byte.class, size);
431         } catch (IllegalAccessException e) {
432             throw new Error(e);
433         } catch (InvocationTargetException e) {
434             throw new Error(e);
435         }
436     }
437 
438     static ByteBuffer newDirectBuffer(long address, int capacity) {
439         ObjectUtil.checkPositiveOrZero(capacity, "capacity");
440 
441         try {
442             return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR.newInstance(address, capacity);
443         } catch (Throwable cause) {
444             // Not expected to ever throw!
445             if (cause instanceof Error) {
446                 throw (Error) cause;
447             }
448             throw new Error(cause);
449         }
450     }
451 
452     static long directBufferAddress(ByteBuffer buffer) {
453         return getLong(buffer, ADDRESS_FIELD_OFFSET);
454     }
455 
456     static long arrayBaseOffset() {
457         return UNSAFE.arrayBaseOffset(byte[].class);
458     }
459 
460     static Object getObject(Object object, long fieldOffset) {
461         return UNSAFE.getObject(object, fieldOffset);
462     }
463 
464     static int getInt(Object object, long fieldOffset) {
465         return UNSAFE.getInt(object, fieldOffset);
466     }
467 
468     private static long getLong(Object object, long fieldOffset) {
469         return UNSAFE.getLong(object, fieldOffset);
470     }
471 
472     static long objectFieldOffset(Field field) {
473         return UNSAFE.objectFieldOffset(field);
474     }
475 
476     static byte getByte(long address) {
477         return UNSAFE.getByte(address);
478     }
479 
480     static short getShort(long address) {
481         return UNSAFE.getShort(address);
482     }
483 
484     static int getInt(long address) {
485         return UNSAFE.getInt(address);
486     }
487 
488     static long getLong(long address) {
489         return UNSAFE.getLong(address);
490     }
491 
492     static byte getByte(byte[] data, int index) {
493         return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
494     }
495 
496     static short getShort(byte[] data, int index) {
497         return UNSAFE.getShort(data, BYTE_ARRAY_BASE_OFFSET + index);
498     }
499 
500     static int getInt(byte[] data, int index) {
501         return UNSAFE.getInt(data, BYTE_ARRAY_BASE_OFFSET + index);
502     }
503 
504     static long getLong(byte[] data, int index) {
505         return UNSAFE.getLong(data, BYTE_ARRAY_BASE_OFFSET + index);
506     }
507 
508     static void putByte(long address, byte value) {
509         UNSAFE.putByte(address, value);
510     }
511 
512     static void putShort(long address, short value) {
513         UNSAFE.putShort(address, value);
514     }
515 
516     static void putInt(long address, int value) {
517         UNSAFE.putInt(address, value);
518     }
519 
520     static void putLong(long address, long value) {
521         UNSAFE.putLong(address, value);
522     }
523 
524     static void putByte(byte[] data, int index, byte value) {
525         UNSAFE.putByte(data, BYTE_ARRAY_BASE_OFFSET + index, value);
526     }
527 
528     static void putShort(byte[] data, int index, short value) {
529         UNSAFE.putShort(data, BYTE_ARRAY_BASE_OFFSET + index, value);
530     }
531 
532     static void putInt(byte[] data, int index, int value) {
533         UNSAFE.putInt(data, BYTE_ARRAY_BASE_OFFSET + index, value);
534     }
535 
536     static void putLong(byte[] data, int index, long value) {
537         UNSAFE.putLong(data, BYTE_ARRAY_BASE_OFFSET + index, value);
538     }
539 
540     static void copyMemory(long srcAddr, long dstAddr, long length) {
541         //UNSAFE.copyMemory(srcAddr, dstAddr, length);
542         while (length > 0) {
543             long size = Math.min(length, UNSAFE_COPY_THRESHOLD);
544             UNSAFE.copyMemory(srcAddr, dstAddr, size);
545             length -= size;
546             srcAddr += size;
547             dstAddr += size;
548         }
549     }
550 
551     static void copyMemory(Object src, long srcOffset, Object dst, long dstOffset, long length) {
552         //UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, length);
553         while (length > 0) {
554             long size = Math.min(length, UNSAFE_COPY_THRESHOLD);
555             UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, size);
556             length -= size;
557             srcOffset += size;
558             dstOffset += size;
559         }
560     }
561 
562     static void setMemory(long address, long bytes, byte value) {
563         UNSAFE.setMemory(address, bytes, value);
564     }
565 
566     static void setMemory(Object o, long offset, long bytes, byte value) {
567         UNSAFE.setMemory(o, offset, bytes, value);
568     }
569 
570     static boolean isZero(byte[] bytes, int startPos, int length) {
571         if (length <= 0) {
572             return true;
573         }
574         final long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos;
575         int remainingBytes = length & 7;
576         final long end = baseOffset + remainingBytes;
577         for (long i = baseOffset - 8 + length; i >= end; i -= 8) {
578             if (UNSAFE.getLong(bytes, i) != 0) {
579                 return false;
580             }
581         }
582 
583         if (remainingBytes >= 4) {
584             remainingBytes -= 4;
585             if (UNSAFE.getInt(bytes, baseOffset + remainingBytes) != 0) {
586                 return false;
587             }
588         }
589         if (remainingBytes >= 2) {
590             return UNSAFE.getChar(bytes, baseOffset) == 0 &&
591                     (remainingBytes == 2 || bytes[startPos + 2] == 0);
592         }
593         return bytes[startPos] == 0;
594     }
595 
596     static ClassLoader getClassLoader(final Class<?> clazz) {
597         if (System.getSecurityManager() == null) {
598             return clazz.getClassLoader();
599         } else {
600             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
601                 @Override
602                 public ClassLoader run() {
603                     return clazz.getClassLoader();
604                 }
605             });
606         }
607     }
608 
609     static ClassLoader getContextClassLoader() {
610         if (System.getSecurityManager() == null) {
611             return Thread.currentThread().getContextClassLoader();
612         } else {
613             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
614                 @Override
615                 public ClassLoader run() {
616                     return Thread.currentThread().getContextClassLoader();
617                 }
618             });
619         }
620     }
621 
622     static ClassLoader getSystemClassLoader() {
623         if (System.getSecurityManager() == null) {
624             return ClassLoader.getSystemClassLoader();
625         } else {
626             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
627                 @Override
628                 public ClassLoader run() {
629                     return ClassLoader.getSystemClassLoader();
630                 }
631             });
632         }
633     }
634 
635     static int addressSize() {
636         return UNSAFE.addressSize();
637     }
638 
639     static long allocateMemory(long size) {
640         return UNSAFE.allocateMemory(size);
641     }
642 
643     static void freeMemory(long address) {
644         UNSAFE.freeMemory(address);
645     }
646 
647     static long reallocateMemory(long address, long newSize) {
648         return UNSAFE.reallocateMemory(address, newSize);
649     }
650 
651     static boolean isAndroid() {
652         return IS_ANDROID;
653     }
654 
655     private static boolean isAndroid0() {
656         boolean android;
657         try {
658             Class.forName("android.app.Application", false, getSystemClassLoader());
659             android = true;
660         } catch (Throwable ignored) {
661             // Failed to load the class uniquely available in Android.
662             android = false;
663         }
664 
665         if (android) {
666             logger.debug("Platform: Android");
667         }
668         return android;
669     }
670 
671     private static boolean explicitTryReflectionSetAccessible0() {
672         // we disable reflective access
673         return SystemPropertyUtil.getBoolean("io.netty.tryReflectionSetAccessible", javaVersion() < 9);
674     }
675 
676     static boolean isExplicitTryReflectionSetAccessible() {
677         return IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE;
678     }
679 
680     static int javaVersion() {
681         return JAVA_VERSION;
682     }
683 
684     private static int javaVersion0() {
685         final int majorVersion;
686 
687         if (isAndroid0()) {
688             majorVersion = 6;
689         } else {
690             majorVersion = majorVersionFromJavaSpecificationVersion();
691         }
692 
693         logger.debug("Java version: {}", majorVersion);
694 
695         return majorVersion;
696     }
697 
698     // Package-private for testing only
699     static int majorVersionFromJavaSpecificationVersion() {
700         return majorVersion(SystemPropertyUtil.get("java.specification.version", "1.6"));
701     }
702 
703     // Package-private for testing only
704     static int majorVersion(final String javaSpecVersion) {
705         final String[] components = javaSpecVersion.split("\\.");
706         final int[] version = new int[components.length];
707         for (int i = 0; i < components.length; i++) {
708             version[i] = Integer.parseInt(components[i]);
709         }
710 
711         if (version[0] == 1) {
712             assert version[1] >= 6;
713             return version[1];
714         } else {
715             return version[0];
716         }
717     }
718 
719     private PlatformDependent0() {
720     }
721 
722 }