/* * Copyright 2011 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package java.lang; /*-[ #ifndef TARGET_OS_SIMULATOR #define TARGET_OS_SIMULATOR 0 #endif #if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR #import <UIKit/UIKit.h> #endif #import "IOSArray_PackagePrivate.h" #import "IOSObjectArray.h" #import "IOSPrimitiveArray.h" #import "NSDictionaryMap.h" #import "java/lang/ArrayIndexOutOfBoundsException.h" #import "java/lang/ArrayStoreException.h" #import "java/lang/IllegalArgumentException.h" #import "java/lang/NullPointerException.h" #import "java/util/Collections.h" #include "mach/mach_time.h" #include "TargetConditionals.h" #if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR #include <crt_externs.h> #endif ]-*/ import java.io.BufferedInputStream; import java.io.Console; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.PrintStream; import java.util.Map; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; /** * Simple iOS version of java.lang.System. No code was shared, just its * public API. * * @author Tom Ball */ public class System { private static Properties props; public static final InputStream in; public static final PrintStream out; public static final PrintStream err; /*-[ static mach_timebase_info_data_t machTimeInfo_; ]-*/ static { // Set up standard in, out, and err. err = new PrintStream(new FileOutputStream(FileDescriptor.err)); out = new PrintStream(new FileOutputStream(FileDescriptor.out)); in = new BufferedInputStream(new FileInputStream(FileDescriptor.in)); // Set up statics for time unit conversion. setTimeInfoConsts(); } private static native void setTimeInfoConsts() /*-[ // Get the timebase info mach_timebase_info(&machTimeInfo_); ]-*/; public static native void setIn(InputStream newIn) /*-[ #if __has_feature(objc_arc) JavaLangSystem_in = newIn; #else JreStrongAssign(&JavaLangSystem_in, newIn); #endif ]-*/; public static native void setOut(java.io.PrintStream newOut) /*-[ #if __has_feature(objc_arc) JavaLangSystem_out = newOut; #else JreStrongAssign(&JavaLangSystem_out, newOut); #endif ]-*/; public static native void setErr(java.io.PrintStream newErr) /*-[ #if __has_feature(objc_arc) JavaLangSystem_err = newErr; #else JreStrongAssign(&JavaLangSystem_err, newErr); #endif ]-*/; public static native long currentTimeMillis() /*-[ return (long long) ((CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) * 1000); ]-*/; public static native int identityHashCode(Object anObject) /*-[ return (int) (intptr_t) anObject; ]-*/; public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) /*-[ if (!src || !dest) { @throw AUTORELEASE([[JavaLangNullPointerException alloc] init]); } Class srcCls = object_getClass(src); Class destCls = object_getClass(dest); if (class_getSuperclass(srcCls) != [IOSArray class]) { NSString *msg = [NSString stringWithFormat:@"source of type %@ is not an array", [src class]]; @throw AUTORELEASE([[JavaLangArrayStoreException alloc] initWithNSString:msg]); } if (destCls != srcCls) { NSString *msg = [NSString stringWithFormat:@"source type %@ cannot be copied to array of type %@", [src class], [dest class]]; @throw AUTORELEASE([[JavaLangArrayStoreException alloc] initWithNSString:msg]); } // Range tests are done by array class. [(IOSArray *) src arraycopy:srcPos destination:(IOSArray *) dest dstOffset:destPos length:length]; ]-*/; public native static long nanoTime() /*-[ uint64_t time = mach_absolute_time(); // Convert to nanoseconds and return, return (time * machTimeInfo_.numer) / machTimeInfo_.denom; ]-*/; public native static void exit(int status) /*-[ exit(status); ]-*/; public native static Properties getProperties() /*-[ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ JreStrongAssignAndConsume(&JavaLangSystem_props, [[JavaUtilProperties alloc] init]); [JavaLangSystem_props setPropertyWithNSString:@"java.class.path" withNSString:@""]; [JavaLangSystem_props setPropertyWithNSString:@"java.class.version" withNSString:@"0"]; [JavaLangSystem_props setPropertyWithNSString:@"java.compiler" withNSString:@""]; [JavaLangSystem_props setPropertyWithNSString:@"java.ext.dirs" withNSString:@""]; [JavaLangSystem_props setPropertyWithNSString:@"java.library.path" withNSString:@""]; [JavaLangSystem_props setPropertyWithNSString:@"java.specification.name" withNSString:@"J2ObjC"]; [JavaLangSystem_props setPropertyWithNSString:@"java.specification.vendor" withNSString:@"J2ObjC"]; [JavaLangSystem_props setPropertyWithNSString:@"java.specification.version" withNSString:@"0"]; [JavaLangSystem_props setPropertyWithNSString:@"java.vendor" withNSString:@"J2ObjC"]; [JavaLangSystem_props setPropertyWithNSString:@"java.vendor.url" withNSString:@"http://j2objc.org/"]; [JavaLangSystem_props setPropertyWithNSString:@"java.version" withNSString:@"0"]; [JavaLangSystem_props setPropertyWithNSString:@"java.vm.name" withNSString:@""]; [JavaLangSystem_props setPropertyWithNSString:@"java.vm.specification.name" withNSString:@"J2ObjC"]; [JavaLangSystem_props setPropertyWithNSString:@"java.vm.specification.vendor" withNSString:@"J2ObjC"]; [JavaLangSystem_props setPropertyWithNSString:@"java.vm.specification.version" withNSString:@"0"]; [JavaLangSystem_props setPropertyWithNSString:@"java.vm.vendor" withNSString:@"J2ObjC"]; [JavaLangSystem_props setPropertyWithNSString:@"java.vm.version" withNSString:@"0"]; // Get os.arch from J2OBJC_BUILD_ARCH defined in fat_lib.mk. #define J2OBJC_BUILD_ARCH_STRINGIFY(x) #x #define J2OBJC_BUILD_ARCH_CSTR(x) J2OBJC_BUILD_ARCH_STRINGIFY(x) #define J2OBJC_BUILD_ARCH_NSSTR ([NSString stringWithUTF8String: \ J2OBJC_BUILD_ARCH_CSTR(J2OBJC_BUILD_ARCH)]) [JavaLangSystem_props setPropertyWithNSString:@"os.arch" withNSString:J2OBJC_BUILD_ARCH_NSSTR]; #undef J2OBJC_BUILD_ARCH_NSSTR #undef J2OBJC_BUILD_ARCH_CSTR #undef J2OBJC_BUILD_ARCH_STRINGIFY NSString *versionString; #if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR BOOL onSimulator = false; #endif // During compile time, see if [NSProcessInfo processInfo].operatingSystemVersion is available // in the SDK. #if (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && (__MAC_OS_X_VERSION_MAX_ALLOWED > __MAC_10_9)) \ || (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) \ && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0)) // Then we check if the method is actually available on the running device. if ([NSProcessInfo instancesRespondToSelector:@selector(operatingSystemVersion)]) { NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion; // This matches the format of [UIDevice currentDevice].systemVersion. if (version.patchVersion) { versionString = [NSString stringWithFormat:@"%ld.%ld.%ld", (long) version.majorVersion, (long) version.minorVersion, (long) version.patchVersion]; } else { versionString = [NSString stringWithFormat:@"%ld.%ld", (long) version.majorVersion, (long) version.minorVersion]; } } else { #else { #endif // #if (defined(...)) #if (TARGET_OS_IPHONE || TARGET_OS_SIMULATOR) // If [NSProcessInfo processInfo].operatingSystemVersion is not available in the SDK and // this is iOS SDK, use [UIDevice currentDevice]. #if TARGET_OS_WATCH versionString = [NSProcessInfo processInfo].operatingSystemVersionString; #else versionString = [UIDevice currentDevice].systemVersion; #endif // #if TARGET_OS_WATCH #else // If we arrive here, we want to try again to see if [UIDevice currentDevice] is // available. This is because the code may be running in a 64-bit iOS Simulator, but // the x86_64 portion of the fat library is built as a Mac library. Class uiDeviceClass = NSClassFromString(@"UIDevice"); SEL currentDeviceSel = NSSelectorFromString(@"currentDevice"); SEL systemVersionSel = NSSelectorFromString(@"systemVersion"); id currentDevice = [uiDeviceClass performSelector:currentDeviceSel]; versionString = (NSString *)[currentDevice performSelector:systemVersionSel]; if (versionString) { onSimulator = true; } else { // Ok, this is OS X. We use operatingSystemVersionString which gives us a localized // version not suitable for parsing. Given the use case of this property, it's not worth // doing more than just reporting this back verbatim. versionString = [NSProcessInfo processInfo].operatingSystemVersionString; } #endif // #if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR } [JavaLangSystem_props setPropertyWithNSString:@"os.version" withNSString:versionString]; [JavaLangSystem_props setPropertyWithNSString:@"file.separator" withNSString:@"/"]; [JavaLangSystem_props setPropertyWithNSString:@"line.separator" withNSString:@"\n"]; [JavaLangSystem_props setPropertyWithNSString:@"path.separator" withNSString:@":"]; [JavaLangSystem_props setPropertyWithNSString:@"org.xml.sax.driver" withNSString:@"org.xmlpull.v1.sax2.Driver"]; NSString *homeDirectory = NSHomeDirectory(); [JavaLangSystem_props setPropertyWithNSString:@"user.home" withNSString:homeDirectory]; NSString *userName = NSUserName(); #if TARGET_OS_SIMULATOR // Some simulators don't initialize the user name, so try hacking it from the app's path. if (!userName || userName.length == 0) { NSArray *bundlePathComponents = [NSBundle.mainBundle.bundlePath pathComponents]; if (bundlePathComponents.count >= 3 && [bundlePathComponents[0] isEqualToString:@"/"] && [bundlePathComponents[1] isEqualToString:@"Users"]) { userName = bundlePathComponents[2]; } } #endif [JavaLangSystem_props setPropertyWithNSString:@"user.name" withNSString:userName]; #if TARGET_OS_SIMULATOR [JavaLangSystem_props setPropertyWithNSString:@"os.name" withNSString:@"iPhone Simulator"]; [JavaLangSystem_props setPropertyWithNSString:@"user.dir" withNSString:[homeDirectory stringByAppendingString:@"/Documents"]]; #elif TARGET_OS_IPHONE [JavaLangSystem_props setPropertyWithNSString:@"os.name" withNSString:@"iPhone"]; [JavaLangSystem_props setPropertyWithNSString:@"user.dir" withNSString:[homeDirectory stringByAppendingString:@"/Documents"]]; #else if (onSimulator) { [JavaLangSystem_props setPropertyWithNSString:@"os.name" withNSString:@"iPhone Simulator"]; [JavaLangSystem_props setPropertyWithNSString:@"user.dir" withNSString:[homeDirectory stringByAppendingString:@"/Documents"]]; } else { [JavaLangSystem_props setPropertyWithNSString:@"os.name" withNSString:@"Mac OS X"]; NSString *curDir = [[NSFileManager defaultManager] currentDirectoryPath]; if ([curDir isEqualToString:@"/"]) { // Workaround for simulator bug. curDir = [homeDirectory stringByAppendingString:@"/Documents"]; } [JavaLangSystem_props setPropertyWithNSString:@"user.dir" withNSString:curDir]; } #endif NSString *tmpDir = NSTemporaryDirectory(); int iLast = (int) [tmpDir length] - 1; if (iLast >= 0 && [tmpDir characterAtIndex:iLast] == '/') { tmpDir = [tmpDir substringToIndex:iLast]; } [JavaLangSystem_props setPropertyWithNSString:@"java.io.tmpdir" withNSString:tmpDir]; [JavaLangSystem_props setPropertyWithNSString:@"java.home" withNSString:[[NSBundle mainBundle] bundlePath]]; char *fileEncoding = getenv("file_encoding"); // Shell variables cannot have periods. if (!fileEncoding) { fileEncoding = getenv("file.encoding"); } if (!fileEncoding) { fileEncoding = "UTF8"; } NSString *enc = [NSString stringWithUTF8String:fileEncoding]; [JavaLangSystem_props setPropertyWithNSString:@"file.encoding" withNSString:enc]; // These properties are used to define the default Locale. NSString *localeId = [[NSLocale currentLocale] localeIdentifier]; NSDictionary *components = [NSLocale componentsFromLocaleIdentifier:localeId]; NSString *language = [components objectForKey:NSLocaleLanguageCode]; if (language) { [JavaLangSystem_props setPropertyWithNSString:@"user.language" withNSString:language]; } NSString *country = [components objectForKey:NSLocaleCountryCode]; if (country) { [JavaLangSystem_props setPropertyWithNSString:@"user.region" withNSString:country]; } NSString *variant = [components objectForKey:NSLocaleVariantCode]; if (variant) { [JavaLangSystem_props setPropertyWithNSString:@"user.variant" withNSString:variant]; } }); return JavaLangSystem_props; ]-*/; public static String getProperty(String key) { return getProperties().getProperty(key); } public static String getProperty(String key, String defaultValue) { String result = getProperties().getProperty(key); return result != null ? result : defaultValue; } public static String setProperty(String key, String value) { return (String) getProperties().setProperty(key, value); } public static void setProperties(Properties properties) { props = properties; } public static String clearProperty(String key) { Properties properties = getProperties(); String result = properties.getProperty(key); properties.remove(key); return result; } public static native String getenv(String name) /*-[ const char *value = getenv([name UTF8String]); return value ? [NSString stringWithUTF8String:value] : nil; ]-*/; public static native Map<String,String> getenv() /*-[ NSDictionaryMap *environmentMap = [NSDictionaryMap mapWithDictionary:[NSProcessInfo processInfo].environment]; return [JavaUtilCollections unmodifiableMapWithJavaUtilMap:environmentMap]; ]-*/; /** * Returns null. Android does not use {@code SecurityManager}. This method * is only provided for source compatibility. * * @return null */ public static SecurityManager getSecurityManager() { return null; } /** * Returns the system's line separator. * @since 1.7 */ public static String lineSeparator() { return "\n"; // Always return OSX/iOS value. } /** * See {@link Runtime#load}. */ public static void load(String pathName) { Runtime.getRuntime().load(pathName); } /** * See {@link Runtime#loadLibrary}. */ public static void loadLibrary(String libName) { Runtime.getRuntime().loadLibrary(libName); } public static void gc() { Runtime.getRuntime().gc(); } /** * No-op on iOS, since it doesn't use garbage collection. */ public static void runFinalization() {} /** * No-op on iOS, since it doesn't use garbage collection. */ public static void runFinalizersOnExit(boolean b) {} /** * Returns the {@link java.io.Console} associated with this VM, or null. * Not all VMs will have an associated console. A console is typically only * available for programs run from the command line. * @since 1.6 */ public static Console console() { return Console.getConsole(); } // Android internal logging methods, rewritten to use Logger. /** * @hide internal use only */ public static void logE(String message) { log(Level.SEVERE, message, null); } /** * @hide internal use only */ public static void logE(String message, Throwable th) { log(Level.SEVERE, message, th); } /** * @hide internal use only */ public static void logI(String message) { log(Level.INFO, message, null); } /** * @hide internal use only */ public static void logI(String message, Throwable th) { log(Level.INFO, message, th); } /** * @hide internal use only */ public static void logW(String message) { log(Level.WARNING, message, null); } /** * @hide internal use only */ public static void logW(String message, Throwable th) { log(Level.WARNING, message, th); } private static Logger systemLogger; private static void log(Level level, String message, Throwable thrown) { if (systemLogger == null) { systemLogger = Logger.getLogger("java.lang.System"); } systemLogger.log(level, message, thrown); } }