• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  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 /*
18  * Copyright (C) 2008 The Android Open Source Project
19  *
20  * Licensed under the Apache License, Version 2.0 (the "License");
21  * you may not use this file except in compliance with the License.
22  * You may obtain a copy of the License at
23  *
24  *      http://www.apache.org/licenses/LICENSE-2.0
25  *
26  * Unless required by applicable law or agreed to in writing, software
27  * distributed under the License is distributed on an "AS IS" BASIS,
28  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29  * See the License for the specific language governing permissions and
30  * limitations under the License.
31  */
32 
33 package java.lang;
34 
35 import dalvik.system.VMRuntime;
36 import dalvik.system.VMStack;
37 import java.io.BufferedInputStream;
38 import java.io.Console;
39 import java.io.FileDescriptor;
40 import java.io.FileInputStream;
41 import java.io.FileOutputStream;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.io.PrintStream;
45 import java.nio.channels.Channel;
46 import java.nio.channels.spi.SelectorProvider;
47 import java.util.AbstractMap;
48 import java.util.Collections;
49 import java.util.HashMap;
50 import java.util.Map;
51 import java.util.Properties;
52 import java.util.Set;
53 import libcore.icu.ICU;
54 import libcore.io.Libcore;
55 import libcore.io.StructUtsname;
56 import libcore.util.ZoneInfoDB;
57 
58 /**
59  * Provides access to system-related information and resources including
60  * standard input and output. Enables clients to dynamically load native
61  * libraries. All methods of this class are accessed in a static way and the
62  * class itself can not be instantiated.
63  *
64  * @see Runtime
65  */
66 public final class System {
67 
68     /**
69      * Default input stream.
70      */
71     public static final InputStream in;
72 
73     /**
74      * Default output stream.
75      */
76     public static final PrintStream out;
77 
78     /**
79      * Default error output stream.
80      */
81     public static final PrintStream err;
82 
83     private static final String lineSeparator;
84     private static Properties systemProperties;
85 
86     static {
87         err = new PrintStream(new FileOutputStream(FileDescriptor.err));
88         out = new PrintStream(new FileOutputStream(FileDescriptor.out));
89         in = new BufferedInputStream(new FileInputStream(FileDescriptor.in));
90         lineSeparator = System.getProperty("line.separator");
91     }
92 
93     /**
94      * Sets the standard input stream to the given user defined input stream.
95      *
96      * @param newIn
97      *            the user defined input stream to set as the standard input
98      *            stream.
99      */
setIn(InputStream newIn)100     public static void setIn(InputStream newIn) {
101         setFieldImpl("in", "Ljava/io/InputStream;", newIn);
102     }
103 
104     /**
105      * Sets the standard output stream to the given user defined output stream.
106      *
107      * @param newOut
108      *            the user defined output stream to set as the standard output
109      *            stream.
110      */
setOut(PrintStream newOut)111     public static void setOut(PrintStream newOut) {
112         setFieldImpl("out", "Ljava/io/PrintStream;", newOut);
113     }
114 
115     /**
116      * Sets the standard error output stream to the given user defined output
117      * stream.
118      *
119      * @param newErr
120      *            the user defined output stream to set as the standard error
121      *            output stream.
122      */
setErr(PrintStream newErr)123     public static void setErr(PrintStream newErr) {
124         setFieldImpl("err", "Ljava/io/PrintStream;", newErr);
125     }
126 
127     /**
128      * Prevents this class from being instantiated.
129      */
System()130     private System() {
131     }
132 
133     /**
134      * Copies {@code length} elements from the array {@code src},
135      * starting at offset {@code srcPos}, into the array {@code dst},
136      * starting at offset {@code dstPos}.
137      *
138      * <p>The source and destination arrays can be the same array,
139      * in which case copying is performed as if the source elements
140      * are first copied into a temporary array and then into the
141      * destination array.
142      *
143      * @param src
144      *            the source array to copy the content.
145      * @param srcPos
146      *            the starting index of the content in {@code src}.
147      * @param dst
148      *            the destination array to copy the data into.
149      * @param dstPos
150      *            the starting index for the copied content in {@code dst}.
151      * @param length
152      *            the number of elements to be copied.
153      */
arraycopy(Object src, int srcPos, Object dst, int dstPos, int length)154     public static native void arraycopy(Object src, int srcPos, Object dst, int dstPos, int length);
155 
156     /**
157      * Returns the current time in milliseconds since January 1, 1970 00:00:00.0 UTC.
158      *
159      * <p>This method always returns UTC times, regardless of the system's time zone.
160      * This is often called "Unix time" or "epoch time".
161      * Use a {@link java.text.DateFormat} instance to format this time for display to a human.
162      *
163      * <p>This method shouldn't be used for measuring timeouts or
164      * other elapsed time measurements, as changing the system time can affect
165      * the results. Use {@link #nanoTime} for that.
166      */
currentTimeMillis()167     public static native long currentTimeMillis();
168 
169     /**
170      * Returns the current timestamp of the most precise timer available on the
171      * local system, in nanoseconds. Equivalent to Linux's {@code CLOCK_MONOTONIC}.
172      *
173      * <p>This timestamp should only be used to measure a duration by comparing it
174      * against another timestamp from the same process on the same device.
175      * Values returned by this method do not have a defined correspondence to
176      * wall clock times; the zero value is typically whenever the device last booted.
177      * Use {@link #currentTimeMillis} if you want to know what time it is.
178      */
nanoTime()179     public static native long nanoTime();
180 
181     /**
182      * Causes the VM to stop running and the program to exit. If
183      * {@link #runFinalizersOnExit(boolean)} has been previously invoked with a
184      * {@code true} argument, then all objects will be properly
185      * garbage-collected and finalized first.
186      *
187      * @param code
188      *            the return code.
189      */
exit(int code)190     public static void exit(int code) {
191         Runtime.getRuntime().exit(code);
192     }
193 
194     /**
195      * Indicates to the VM that it would be a good time to run the
196      * garbage collector. Note that this is a hint only. There is no guarantee
197      * that the garbage collector will actually be run.
198      */
gc()199     public static void gc() {
200         Runtime.getRuntime().gc();
201     }
202 
203     /**
204      * Returns the value of the environment variable with the given name {@code
205      * var}.
206      *
207      * @param name
208      *            the name of the environment variable.
209      * @return the value of the specified environment variable or {@code null}
210      *         if no variable exists with the given name.
211      */
getenv(String name)212     public static String getenv(String name) {
213         return getenv(name, null);
214     }
215 
getenv(String name, String defaultValue)216     private static String getenv(String name, String defaultValue) {
217         if (name == null) {
218             throw new NullPointerException("name == null");
219         }
220         String value = Libcore.os.getenv(name);
221         return (value != null) ? value : defaultValue;
222     }
223 
224     /**
225      * Returns an unmodifiable map of all available environment variables.
226      *
227      * @return the map representing all environment variables.
228      */
getenv()229     public static Map<String, String> getenv() {
230         Map<String, String> map = new HashMap<String, String>();
231         for (String entry : Libcore.os.environ()) {
232             int index = entry.indexOf('=');
233             if (index != -1) {
234                 map.put(entry.substring(0, index), entry.substring(index + 1));
235             }
236         }
237         return new SystemEnvironment(map);
238     }
239 
240     /**
241      * Returns the inherited channel from the creator of the current virtual
242      * machine.
243      *
244      * @return the inherited {@link Channel} or {@code null} if none exists.
245      * @throws IOException
246      *             if an I/O error occurred.
247      * @see SelectorProvider
248      * @see SelectorProvider#inheritedChannel()
249      */
inheritedChannel()250     public static Channel inheritedChannel() throws IOException {
251         return SelectorProvider.provider().inheritedChannel();
252     }
253 
254     /**
255      * Returns the system properties. Note that this is not a copy, so that
256      * changes made to the returned Properties object will be reflected in
257      * subsequent calls to getProperty and getProperties.
258      *
259      * @return the system properties.
260      */
getProperties()261     public static Properties getProperties() {
262         if (systemProperties == null) {
263             initSystemProperties();
264         }
265         return systemProperties;
266     }
267 
initSystemProperties()268     private static void initSystemProperties() {
269         VMRuntime runtime = VMRuntime.getRuntime();
270         Properties p = new Properties();
271 
272         String projectUrl = "http://www.android.com/";
273         String projectName = "The Android Project";
274 
275         p.put("java.boot.class.path", runtime.bootClassPath());
276         p.put("java.class.path", runtime.classPath());
277 
278         // None of these four are meaningful on Android, but these keys are guaranteed
279         // to be present for System.getProperty. For java.class.version, we use the maximum
280         // class file version that dx currently supports.
281         p.put("java.class.version", "50.0");
282         p.put("java.compiler", "");
283         p.put("java.ext.dirs", "");
284         p.put("java.version", "0");
285 
286         p.put("java.home", getenv("JAVA_HOME", "/system"));
287 
288         p.put("java.io.tmpdir", "/tmp");
289         p.put("java.library.path", getenv("LD_LIBRARY_PATH"));
290 
291         p.put("java.specification.name", "Dalvik Core Library");
292         p.put("java.specification.vendor", projectName);
293         p.put("java.specification.version", "0.9");
294 
295         p.put("java.vendor", projectName);
296         p.put("java.vendor.url", projectUrl);
297         p.put("java.vm.name", "Dalvik");
298         p.put("java.vm.specification.name", "Dalvik Virtual Machine Specification");
299         p.put("java.vm.specification.vendor", projectName);
300         p.put("java.vm.specification.version", "0.9");
301         p.put("java.vm.vendor", projectName);
302         p.put("java.vm.version", runtime.vmVersion());
303 
304         p.put("file.separator", "/");
305         p.put("line.separator", "\n");
306         p.put("path.separator", ":");
307 
308         p.put("java.runtime.name", "Android Runtime");
309         p.put("java.runtime.version", "0.9");
310         p.put("java.vm.vendor.url", projectUrl);
311 
312         p.put("file.encoding", "UTF-8");
313         p.put("user.language", "en");
314         p.put("user.region", "US");
315 
316         p.put("user.home", getenv("HOME", ""));
317         p.put("user.name", getenv("USER", ""));
318 
319         StructUtsname info = Libcore.os.uname();
320         p.put("os.arch", info.machine);
321         p.put("os.name", info.sysname);
322         p.put("os.version", info.release);
323 
324         // Undocumented Android-only properties.
325         p.put("android.icu.library.version", ICU.getIcuVersion());
326         p.put("android.icu.unicode.version", ICU.getUnicodeVersion());
327         p.put("android.icu.cldr.version", ICU.getCldrVersion());
328         // TODO: it would be nice to have this but currently it causes circularity.
329         // p.put("android.tzdata.version", ZoneInfoDB.getVersion());
330         parsePropertyAssignments(p, specialProperties());
331 
332         // Override built-in properties with settings from the command line.
333         parsePropertyAssignments(p, runtime.properties());
334 
335         systemProperties = p;
336     }
337 
338     /**
339      * Returns an array of "key=value" strings containing information not otherwise
340      * easily available, such as #defined library versions.
341      */
specialProperties()342     private static native String[] specialProperties();
343 
344     /**
345      * Adds each element of 'assignments' to 'p', treating each element as an
346      * assignment in the form "key=value".
347      */
parsePropertyAssignments(Properties p, String[] assignments)348     private static void parsePropertyAssignments(Properties p, String[] assignments) {
349         for (String assignment : assignments) {
350             int split = assignment.indexOf('=');
351             String key = assignment.substring(0, split);
352             String value = assignment.substring(split + 1);
353             p.put(key, value);
354         }
355     }
356 
357     /**
358      * Returns the value of a particular system property or {@code null} if no
359      * such property exists.
360      *
361      * <p>The following properties are always provided by the Dalvik VM:
362      * <p><table BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
363      * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
364      *     <td><b>Name</b></td>        <td><b>Meaning</b></td>                    <td><b>Example</b></td></tr>
365      * <tr><td>file.separator</td>     <td>{@link java.io.File#separator}</td>    <td>{@code /}</td></tr>
366      *
367      * <tr><td>java.class.path</td>    <td>System class path</td>                 <td>{@code .}</td></tr>
368      * <tr><td>java.class.version</td> <td>(Not useful on Android)</td>           <td>{@code 50.0}</td></tr>
369      * <tr><td>java.compiler</td>      <td>(Not useful on Android)</td>           <td>Empty</td></tr>
370      * <tr><td>java.ext.dirs</td>      <td>(Not useful on Android)</td>           <td>Empty</td></tr>
371      * <tr><td>java.home</td>          <td>Location of the VM on the file system</td> <td>{@code /system}</td></tr>
372      * <tr><td>java.io.tmpdir</td>     <td>See {@link java.io.File#createTempFile}</td> <td>{@code /sdcard}</td></tr>
373      * <tr><td>java.library.path</td>  <td>Search path for JNI libraries</td>     <td>{@code /system/lib}</td></tr>
374      * <tr><td>java.vendor</td>        <td>Human-readable VM vendor</td>          <td>{@code The Android Project}</td></tr>
375      * <tr><td>java.vendor.url</td>    <td>URL for VM vendor's web site</td>      <td>{@code http://www.android.com/}</td></tr>
376      * <tr><td>java.version</td>       <td>(Not useful on Android)</td>           <td>{@code 0}</td></tr>
377      *
378      * <tr><td>java.specification.version</td>    <td>VM libraries version</td>        <td>{@code 0.9}</td></tr>
379      * <tr><td>java.specification.vendor</td>     <td>VM libraries vendor</td>         <td>{@code The Android Project}</td></tr>
380      * <tr><td>java.specification.name</td>       <td>VM libraries name</td>           <td>{@code Dalvik Core Library}</td></tr>
381      * <tr><td>java.vm.version</td>               <td>VM implementation version</td>   <td>{@code 1.2.0}</td></tr>
382      * <tr><td>java.vm.vendor</td>                <td>VM implementation vendor</td>    <td>{@code The Android Project}</td></tr>
383      * <tr><td>java.vm.name</td>                  <td>VM implementation name</td>      <td>{@code Dalvik}</td></tr>
384      * <tr><td>java.vm.specification.version</td> <td>VM specification version</td>    <td>{@code 0.9}</td></tr>
385      * <tr><td>java.vm.specification.vendor</td>  <td>VM specification vendor</td>     <td>{@code The Android Project}</td></tr>
386      * <tr><td>java.vm.specification.name</td>    <td>VM specification name</td>       <td>{@code Dalvik Virtual Machine Specification}</td></tr>
387      *
388      * <tr><td>line.separator</td>     <td>The system line separator</td>         <td>{@code \n}</td></tr>
389      *
390      * <tr><td>os.arch</td>            <td>OS architecture</td>                   <td>{@code armv7l}</td></tr>
391      * <tr><td>os.name</td>            <td>OS (kernel) name</td>                  <td>{@code Linux}</td></tr>
392      * <tr><td>os.version</td>         <td>OS (kernel) version</td>               <td>{@code 2.6.32.9-g103d848}</td></tr>
393      *
394      * <tr><td>path.separator</td>     <td>See {@link java.io.File#pathSeparator}</td> <td>{@code :}</td></tr>
395      *
396      * <tr><td>user.dir</td>           <td>Base of non-absolute paths</td>        <td>{@code /}</td></tr>
397      * <tr><td>user.home</td>          <td>(Not useful on Android)</td>           <td>Empty</td></tr>
398      * <tr><td>user.name</td>          <td>(Not useful on Android)</td>           <td>Empty</td></tr>
399      *
400      * </table>
401      *
402      * <p>It is a mistake to try to override any of these. Doing so will have unpredictable results.
403      *
404      * @param propertyName
405      *            the name of the system property to look up.
406      * @return the value of the specified system property or {@code null} if the
407      *         property doesn't exist.
408      */
getProperty(String propertyName)409     public static String getProperty(String propertyName) {
410         return getProperty(propertyName, null);
411     }
412 
413     /**
414      * Returns the value of a particular system property. The {@code
415      * defaultValue} will be returned if no such property has been found.
416      */
getProperty(String name, String defaultValue)417     public static String getProperty(String name, String defaultValue) {
418         checkPropertyName(name);
419         return getProperties().getProperty(name, defaultValue);
420     }
421 
422     /**
423      * Sets the value of a particular system property.
424      *
425      * @return the old value of the property or {@code null} if the property
426      *         didn't exist.
427      */
setProperty(String name, String value)428     public static String setProperty(String name, String value) {
429         checkPropertyName(name);
430         return (String) getProperties().setProperty(name, value);
431     }
432 
433     /**
434      * Removes a specific system property.
435      *
436      * @return the property value or {@code null} if the property didn't exist.
437      * @throws NullPointerException
438      *             if the argument is {@code null}.
439      * @throws IllegalArgumentException
440      *             if the argument is empty.
441      */
clearProperty(String name)442     public static String clearProperty(String name) {
443         checkPropertyName(name);
444         return (String) getProperties().remove(name);
445     }
446 
checkPropertyName(String name)447     private static void checkPropertyName(String name) {
448         if (name == null) {
449             throw new NullPointerException("name == null");
450         }
451         if (name.isEmpty()) {
452             throw new IllegalArgumentException("name is empty");
453         }
454     }
455 
456     /**
457      * Returns the {@link java.io.Console} associated with this VM, or null.
458      * Not all VMs will have an associated console. A console is typically only
459      * available for programs run from the command line.
460      * @since 1.6
461      */
console()462     public static Console console() {
463         return Console.getConsole();
464     }
465 
466     /**
467      * Returns null. Android does not use {@code SecurityManager}. This method
468      * is only provided for source compatibility.
469      *
470      * @return null
471      */
getSecurityManager()472     public static SecurityManager getSecurityManager() {
473         return null;
474     }
475 
476     /**
477      * Returns an integer hash code for the parameter. The hash code returned is
478      * the same one that would be returned by the method {@code
479      * java.lang.Object.hashCode()}, whether or not the object's class has
480      * overridden hashCode(). The hash code for {@code null} is {@code 0}.
481      *
482      * @param anObject
483      *            the object to calculate the hash code.
484      * @return the hash code for the given object.
485      * @see java.lang.Object#hashCode
486      */
identityHashCode(Object anObject)487     public static native int identityHashCode(Object anObject);
488 
489     /**
490      * Returns the system's line separator. On Android, this is {@code "\n"}. The value
491      * comes from the value of the {@code line.separator} system property when the VM
492      * starts. Later changes to the property will not affect the value returned by this
493      * method.
494      * @since 1.7
495      * @hide 1.7 - fix documentation references to "line.separator" in Formatter.
496      */
lineSeparator()497     public static String lineSeparator() {
498         return lineSeparator;
499     }
500 
501     /**
502      * Loads and links the dynamic library that is identified through the
503      * specified path. This method is similar to {@link #loadLibrary(String)},
504      * but it accepts a full path specification whereas {@code loadLibrary} just
505      * accepts the name of the library to load.
506      *
507      * @param pathName
508      *            the path of the file to be loaded.
509      */
load(String pathName)510     public static void load(String pathName) {
511         Runtime.getRuntime().load(pathName, VMStack.getCallingClassLoader());
512     }
513 
514     /**
515      * Loads and links the library with the specified name. The mapping of the
516      * specified library name to the full path for loading the library is
517      * implementation-dependent.
518      *
519      * @param libName
520      *            the name of the library to load.
521      * @throws UnsatisfiedLinkError
522      *             if the library could not be loaded.
523      */
loadLibrary(String libName)524     public static void loadLibrary(String libName) {
525         Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());
526     }
527 
528     /**
529      * @hide internal use only
530      */
logE(String message)531     public static void logE(String message) {
532         log('E', message, null);
533     }
534 
535     /**
536      * @hide internal use only
537      */
logE(String message, Throwable th)538     public static void logE(String message, Throwable th) {
539         log('E', message, th);
540     }
541 
542     /**
543      * @hide internal use only
544      */
logI(String message)545     public static void logI(String message) {
546         log('I', message, null);
547     }
548 
549     /**
550      * @hide internal use only
551      */
logI(String message, Throwable th)552     public static void logI(String message, Throwable th) {
553         log('I', message, th);
554     }
555 
556     /**
557      * @hide internal use only
558      */
logW(String message)559     public static void logW(String message) {
560         log('W', message, null);
561     }
562 
563     /**
564      * @hide internal use only
565      */
logW(String message, Throwable th)566     public static void logW(String message, Throwable th) {
567         log('W', message, th);
568     }
569 
log(char type, String message, Throwable th)570     private static native void log(char type, String message, Throwable th);
571 
572     /**
573      * Provides a hint to the VM that it would be useful to attempt
574      * to perform any outstanding object finalization.
575      */
runFinalization()576     public static void runFinalization() {
577         Runtime.getRuntime().runFinalization();
578     }
579 
580     /**
581      * Ensures that, when the VM is about to exit, all objects are
582      * finalized. Note that all finalization which occurs when the system is
583      * exiting is performed after all running threads have been terminated.
584      *
585      * @param flag
586      *            the flag determines if finalization on exit is enabled.
587      * @deprecated this method is unsafe.
588      */
589     @SuppressWarnings("deprecation")
590     @Deprecated
runFinalizersOnExit(boolean flag)591     public static void runFinalizersOnExit(boolean flag) {
592         Runtime.runFinalizersOnExit(flag);
593     }
594 
595     /**
596      * Sets all system properties. This does not take a copy; the passed-in object is used
597      * directly. Passing null causes the VM to reinitialize the properties to how they were
598      * when the VM was started.
599      */
setProperties(Properties p)600     public static void setProperties(Properties p) {
601         systemProperties = p;
602     }
603 
604     /**
605      * Throws {@code SecurityException}.
606      *
607      * <p>Security managers do <i>not</i> provide a secure environment for
608      * executing untrusted code and are unsupported on Android. Untrusted code
609      * cannot be safely isolated within a single VM on Android, so this method
610      * <i>always</i> throws a {@code SecurityException}.
611      *
612      * @param sm a security manager
613      * @throws SecurityException always
614      */
setSecurityManager(SecurityManager sm)615     public static void setSecurityManager(SecurityManager sm) {
616         if (sm != null) {
617             throw new SecurityException();
618         }
619     }
620 
621     /**
622      * Returns the platform specific file name format for the shared library
623      * named by the argument.
624      *
625      * @param userLibName
626      *            the name of the library to look up.
627      * @return the platform specific filename for the library.
628      */
mapLibraryName(String userLibName)629     public static native String mapLibraryName(String userLibName);
630 
631     /**
632      * Sets the value of the named static field in the receiver to the passed in
633      * argument.
634      *
635      * @param fieldName
636      *            the name of the field to set, one of in, out, or err
637      * @param stream
638      *            the new value of the field
639      */
setFieldImpl(String fieldName, String signature, Object stream)640     private static native void setFieldImpl(String fieldName, String signature, Object stream);
641 
642 
643     /**
644      * The unmodifiable environment variables map. System.getenv() specifies
645      * that this map must throw when passed non-String keys.
646      */
647     static class SystemEnvironment extends AbstractMap<String, String> {
648         private final Map<String, String> map;
649 
SystemEnvironment(Map<String, String> map)650         public SystemEnvironment(Map<String, String> map) {
651             this.map = Collections.unmodifiableMap(map);
652         }
653 
entrySet()654         @Override public Set<Entry<String, String>> entrySet() {
655             return map.entrySet();
656         }
657 
get(Object key)658         @Override public String get(Object key) {
659             return map.get(toNonNullString(key));
660         }
661 
containsKey(Object key)662         @Override public boolean containsKey(Object key) {
663             return map.containsKey(toNonNullString(key));
664         }
665 
containsValue(Object value)666         @Override public boolean containsValue(Object value) {
667             return map.containsValue(toNonNullString(value));
668         }
669 
toNonNullString(Object o)670         private String toNonNullString(Object o) {
671             if (o == null) {
672                 throw new NullPointerException("o == null");
673             }
674             return (String) o;
675         }
676     }
677 }
678