1/**
2 * Copyright 2006-2017 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.objenesis.strategy;
17
18import org.objenesis.ObjenesisException;
19
20import java.lang.reflect.Field;
21
22/**
23 * List of constants describing the currently used platform.
24 *
25 * @author Henri Tremblay
26 */
27public final class PlatformDescription {
28
29   /** JVM_NAME prefix for JRockit */
30   public static final String JROCKIT = "BEA";
31
32   /** JVM_NAME prefix for GCJ */
33   public static final String GNU = "GNU libgcj";
34
35   /** JVM_NAME prefix for Java HotSpot */
36   public static final String HOTSPOT = "Java HotSpot";
37
38   /**
39    * JVM_NAME prefix for Java HotSpot
40    *
41    * @deprecated Use {@link #HOTSPOT} instead
42    */
43   @Deprecated
44   public static final String SUN = HOTSPOT;
45
46   /** JVM_NAME prefix for the OpenJDK */
47   public static final String OPENJDK = "OpenJDK";
48
49   /** JVM_NAME prefix for Aonix PERC */
50   public static final String PERC = "PERC";
51
52   /** JVM_NAME prefix for Dalvik/Android */
53   public static final String DALVIK = "Dalvik";
54
55   /** Java specification version */
56   public static final String SPECIFICATION_VERSION = System
57      .getProperty("java.specification.version");
58
59   /** JVM version */
60   public static final String VM_VERSION = System.getProperty("java.runtime.version");
61
62   /** JVM version */
63   public static final String VM_INFO = System.getProperty("java.vm.info");
64
65   /** VM vendor version */
66   public static final String VENDOR_VERSION = System.getProperty("java.vm.version");
67
68   /** VM vendor name */
69   public static final String VENDOR = System.getProperty("java.vm.vendor");
70
71   /** JVM name */
72   public static final String JVM_NAME = System.getProperty("java.vm.name");
73
74   /** Android version. Will be 0 for none android platform */
75   public static final int ANDROID_VERSION = getAndroidVersion();
76
77   /** Flag telling if this version of Android is based on the OpenJDK */
78   public static final boolean IS_ANDROID_OPENJDK = getIsAndroidOpenJDK();
79
80   /** Google App Engine version or null is we are not on GAE */
81   public static final String GAE_VERSION = getGaeRuntimeVersion();
82
83   /**
84    * Describes the platform. Outputs Java version and vendor.
85    *
86    * @return Description of the current platform
87    */
88   public static String describePlatform() {
89      String desc = "Java " + SPECIFICATION_VERSION + " ("
90              + "VM vendor name=\"" + VENDOR + "\", "
91              + "VM vendor version=" + VENDOR_VERSION + ", "
92              + "JVM name=\"" + JVM_NAME + "\", "
93              + "JVM version=" + VM_VERSION + ", "
94              + "JVM info=" + VM_INFO;
95
96      // Add the API level is it's an Android platform
97      int androidVersion = ANDROID_VERSION;
98      if(androidVersion != 0) {
99         desc += ", API level=" + ANDROID_VERSION;
100      }
101      desc += ")";
102
103      return desc;
104   }
105
106   /**
107    * Check if the current JVM is of the type passed in parameter. Normally, this will be a constant
108    * from this class. We basically do
109    * <code>System.getProperty("java.vm.name").startWith(name)</code>.
110    *
111    * @param name jvm name we are looking for
112    * @return if it's the requested JVM
113    */
114   public static boolean isThisJVM(String name) {
115      return JVM_NAME.startsWith(name);
116   }
117
118   /**
119    * Check if this JVM is an Android JVM based on OpenJDK.
120    *
121    * @return if it's an Android version based on the OpenJDK. Will return false if this JVM isn't an Android JVM at all
122     */
123   public static boolean isAndroidOpenJDK() {
124      return IS_ANDROID_OPENJDK;
125   }
126
127   private static boolean getIsAndroidOpenJDK() {
128      if(getAndroidVersion() == 0) {
129         return false; // Not android at all
130      }
131      // Sadly, Android N is still API 23. So we can't base ourselves on the API level to know if it is an OpenJDK
132      // version or not
133      String bootClasspath = System.getProperty("java.boot.class.path");
134      return bootClasspath != null && bootClasspath.toLowerCase().contains("core-oj.jar");
135   }
136
137   public static boolean isGoogleAppEngine() {
138      return GAE_VERSION != null;
139   }
140
141   private static String getGaeRuntimeVersion() {
142      return System.getProperty("com.google.appengine.runtime.version");
143   }
144
145   private static int getAndroidVersion() {
146      if(!isThisJVM(DALVIK)) {
147         return 0;
148      }
149      return getAndroidVersion0();
150   }
151
152   private static int getAndroidVersion0() {
153      Class<?> clazz;
154      try {
155         clazz = Class.forName("android.os.Build$VERSION");
156      }
157      catch(ClassNotFoundException e) {
158         throw new ObjenesisException(e);
159      }
160      Field field;
161      try {
162         field = clazz.getField("SDK_INT");
163      }
164      catch(NoSuchFieldException e) {
165         // Might be a really old API (before 4), go for SDK
166         return getOldAndroidVersion(clazz);
167      }
168      int version;
169      try {
170         version = (Integer) field.get(null);
171      }
172      catch(IllegalAccessException e) {
173         throw new RuntimeException(e);
174      }
175      return version;
176   }
177
178   private static int getOldAndroidVersion(Class<?> versionClass) {
179      Field field;
180      try {
181         field = versionClass.getField("SDK");
182      }
183      catch(NoSuchFieldException e) {
184         throw new ObjenesisException(e);
185      }
186      String version;
187      try {
188         version = (String) field.get(null);
189      }
190      catch(IllegalAccessException e) {
191         throw new RuntimeException(e);
192      }
193      return Integer.parseInt(version);
194   }
195
196   private PlatformDescription() {
197   }
198}
199