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