• 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 package java.lang;
19 
20 /**
21  * A {@code ThreadGroup} is a means of organizing {@link Thread}s into a
22  * hierarchical structure. A {@code ThreadGroup} can contain zero or more
23  * {@code Thread}s and zero or more other {@code ThreadGroup}s. Each {@code
24  * Thread} and each {@code ThreadGroup} (except the root group) has a unique
25  * parent {@code ThreadGroup}. The result is a tree whose inner nodes are
26  * {@code ThreadGroup}s and whose leaf nodes are {@code Threads}. The unique
27  * root of the tree is a {@code ThreadGroup} that is created at VM startup and
28  * has the name "system". The benefit of using {@code ThreadGroup}s, in addition
29  * to the mere housekeeping aspect, is that all {@code Thread}s in a {@code
30  * ThreadGroup} can be manipulated together, that is, the {@code ThreadGroup}
31  * has methods that delegate to all its all {@code Thread}s.
32  *
33  * @see Thread
34  * @see SecurityManager
35  *
36  * @since Android 1.0
37  */
38 public class ThreadGroup implements Thread.UncaughtExceptionHandler {
39 
40     // Name of this ThreadGroup
41     private String name;
42     // BEGIN android-note
43     // VM needs this field name for debugging.
44     // END android-note
45 
46     // Maximum priority for Threads inside this ThreadGroup
47     private int maxPriority = Thread.MAX_PRIORITY;
48 
49     // The ThreadGroup to which this ThreadGroup belongs
50     ThreadGroup parent;
51     // BEGIN android-note
52     // VM needs this field name for debugging.
53     // END android-note
54 
55     int numThreads;
56 
57     // The Threads this ThreadGroup contains
58     private Thread[] childrenThreads = new Thread[5];
59 
60     // The number of children groups
61     int numGroups;
62 
63     // The ThreadGroups this ThreadGroup contains
64     private ThreadGroup[] childrenGroups = new ThreadGroup[3];
65 
66     // Locked when using the childrenGroups field
67     private class ChildrenGroupsLock {}
68     private Object childrenGroupsLock = new ChildrenGroupsLock();
69 
70     // Locked when using the childrenThreads field
71     private class ChildrenThreadsLock {}
72     private Object childrenThreadsLock = new ChildrenThreadsLock();
73 
74     // Whether this ThreadGroup is a daemon ThreadGroup or not
75     private boolean isDaemon;
76 
77     // Whether this ThreadGroup has already been destroyed or not
78     private boolean isDestroyed;
79 
80     // BEGIN android-added
81     /* the VM uses these directly; do not rename */
82     static ThreadGroup mSystem = new ThreadGroup();
83     static ThreadGroup mMain = new ThreadGroup(mSystem, "main");
84     // END android-added
85 
86     // BEGIN android-removed
87     // /**
88     //  * Used by the JVM to create the "system" ThreadGroup. Construct a
89     //  * ThreadGroup instance, and assign the name "system".
90     //  */
91     // private ThreadGroup() {
92     //     name = "system";
93     // }
94     // END android-removed
95 
96     /**
97      * Constructs a new ThreadGroup with the name provided. The new ThreadGroup
98      * will be child of the ThreadGroup to which the
99      * {@code Thread.currentThread()} belongs.
100      *
101      * @param name the name for the ThreadGroup being created
102      *
103      * @throws SecurityException if {@code checkAccess()} for the parent
104      *         group fails with a SecurityException
105      *
106      * @see java.lang.Thread#currentThread
107      */
108 
ThreadGroup(String name)109     public ThreadGroup(String name) {
110         this(Thread.currentThread().getThreadGroup(), name);
111     }
112 
113     /**
114      * Constructs a new ThreadGroup with the name provided, as child of the
115      * ThreadGroup {@code parent}.
116      *
117      * @param parent the parent ThreadGroup
118      * @param name the name for the ThreadGroup being created
119      *
120      * @throws NullPointerException if {@code parent} is
121      *         {@code null}
122      * @throws SecurityException if {@code checkAccess()} for the parent
123      *         group fails with a SecurityException
124      * @throws IllegalThreadStateException if {@code parent} has been
125      *         destroyed already
126      */
ThreadGroup(ThreadGroup parent, String name)127     public ThreadGroup(ThreadGroup parent, String name) {
128         super();
129         if (Thread.currentThread() != null) {
130             // If parent is null we must throw NullPointerException, but that
131             // will be done "for free" with the message send below
132             parent.checkAccess();
133         }
134 
135         this.name = name;
136         this.setParent(parent);
137         if (parent != null) {
138             this.setMaxPriority(parent.getMaxPriority());
139             if (parent.isDaemon()) {
140                 this.setDaemon(true);
141             }
142         }
143     }
144 
145     /**
146      * Initialize the special "system" ThreadGroup. Was "main" in Harmony,
147      * but we have an additional group above that in Android.
148      */
ThreadGroup()149     ThreadGroup() {
150         this.name = "system";
151         this.setParent(null);
152     }
153 
154     /**
155      * Returns the number of Threads which are children of the receiver,
156      * directly or indirectly and are running.
157      *
158      * @return the number of children Threads
159      */
160 
activeCount()161     public int activeCount() {
162         // BEGIN android-changed
163         int count = 0;
164         // Lock the children thread list
165         synchronized (this.childrenThreadsLock) {
166             for (int i = 0; i < numThreads; i++) {
167                 if(childrenThreads[i].isAlive()) {
168                     count++;
169                 }
170             }
171         }
172         // END android-changed
173         // Lock this subpart of the tree as we walk
174         synchronized (this.childrenGroupsLock) {
175             for (int i = 0; i < numGroups; i++) {
176                 count += this.childrenGroups[i].activeCount();
177             }
178         }
179         return count;
180     }
181 
182     /**
183      * Returns the number of ThreadGroups which are children of the receiver,
184      * directly or indirectly.
185      *
186      * @return the number of children ThreadGroups
187      */
activeGroupCount()188     public int activeGroupCount() {
189         int count = 0;
190         // Lock this subpart of the tree as we walk
191         synchronized (this.childrenGroupsLock) {
192             for (int i = 0; i < numGroups; i++) {
193                 // One for this group & the subgroups
194                 count += 1 + this.childrenGroups[i].activeGroupCount();
195             }
196         }
197         return count;
198     }
199 
200     /**
201      * Adds a Thread to the receiver. This should only be visible to class
202      * java.lang.Thread, and should only be called when a new Thread is created
203      * and initialized by the constructor.
204      *
205      * @param thread Thread to add to the receiver
206      *
207      * @throws IllegalThreadStateException if the receiver has been destroyed
208      *         already
209      *
210      * @see #remove(java.lang.Thread)
211      */
add(Thread thread)212     final void add(Thread thread) throws IllegalThreadStateException {
213         synchronized (this.childrenThreadsLock) {
214             if (!isDestroyed) {
215                 if (childrenThreads.length == numThreads) {
216                     Thread[] newThreads = new Thread[childrenThreads.length * 2];
217                     System.arraycopy(childrenThreads, 0, newThreads, 0, numThreads);
218                     newThreads[numThreads++] = thread;
219                     childrenThreads = newThreads;
220                 } else {
221                     childrenThreads[numThreads++] = thread;
222                 }
223             } else {
224                 throw new IllegalThreadStateException();
225             }
226         }
227     }
228 
229     /**
230      * Adds a ThreadGroup to the receiver.
231      *
232      * @param g ThreadGroup to add to the receiver
233      *
234      * @throws IllegalThreadStateException if the receiver has been destroyed
235      *         already
236      */
add(ThreadGroup g)237     private void add(ThreadGroup g) throws IllegalThreadStateException {
238         synchronized (this.childrenGroupsLock) {
239             if (!isDestroyed) {
240                 if (childrenGroups.length == numGroups) {
241                     ThreadGroup[] newGroups = new ThreadGroup[childrenGroups.length * 2];
242                     System.arraycopy(childrenGroups, 0, newGroups, 0, numGroups);
243                     newGroups[numGroups++] = g;
244                     childrenGroups = newGroups;
245                 } else {
246                     childrenGroups[numGroups++] = g;
247                 }
248             } else {
249                 throw new IllegalThreadStateException();
250             }
251         }
252     }
253 
254     /**
255      * Does nothing. The definition of this method depends on the deprecated
256      * method {@link #suspend()}. The exact behavior of this call was never
257      * specified.
258      *
259      * @param b Used to control low memory implicit suspension
260      * @return {@code true} (always)
261      *
262      * @deprecated Required deprecated method suspend().
263      */
264     @Deprecated
allowThreadSuspension(boolean b)265     public boolean allowThreadSuspension(boolean b) {
266         // Does not apply to this VM, no-op
267         return true;
268     }
269 
270     /**
271      * Checks the accessibility of the ThreadGroup from the perspective of the
272      * caller. If there is a SecurityManager installed, calls
273      * {@code checkAccess} with the receiver as a parameter, otherwise does
274      * nothing.
275      */
checkAccess()276     public final void checkAccess() {
277         // Forwards the message to the SecurityManager (if there's one) passing
278         // the receiver as parameter
279         SecurityManager currentManager = System.getSecurityManager();
280         if (currentManager != null) {
281             currentManager.checkAccess(this);
282         }
283     }
284 
285     /**
286      * Destroys the receiver and recursively all its subgroups. It is only legal
287      * to destroy a ThreadGroup that has no Threads in it. Any daemon
288      * ThreadGroup is destroyed automatically when it becomes empty (no Threads
289      * and no ThreadGroups in it).
290      *
291      * @throws IllegalThreadStateException if the receiver or any of its
292      *         subgroups has been destroyed already or if it still contains
293      *         threads.
294      * @throws SecurityException if {@code this.checkAccess()} fails with
295      *         a SecurityException
296      */
297 
destroy()298     public final void destroy() {
299         checkAccess();
300 
301         // Lock this subpart of the tree as we walk
302         synchronized (this.childrenThreadsLock) {
303             synchronized (this.childrenGroupsLock) {
304                 // BEGIN android-added
305                 if (this.isDestroyed) {
306                     throw new IllegalThreadStateException(
307                             "Thread group was already destroyed: "
308                             + (this.name != null ? this.name : "n/a"));
309                 }
310                 if (this.numThreads > 0) {
311                     throw new IllegalThreadStateException(
312                             "Thread group still contains threads: "
313                             + (this.name != null ? this.name : "n/a"));
314                 }
315                 // END android-added
316                 int toDestroy = numGroups;
317                 // Call recursively for subgroups
318                 for (int i = 0; i < toDestroy; i++) {
319                     // We always get the first element - remember, when the
320                     // child dies it removes itself from our collection. See
321                     // below.
322                     this.childrenGroups[0].destroy();
323                 }
324 
325                 if (parent != null) {
326                     parent.remove(this);
327                 }
328 
329                 // Now that the ThreadGroup is really destroyed it can be tagged
330                 // as so
331                 this.isDestroyed = true;
332             }
333         }
334     }
335 
336     /*
337      * Auxiliary method that destroys the receiver and recursively all its
338      * subgroups if the receiver is a daemon ThreadGroup.
339      *
340      * @see #destroy
341      * @see #setDaemon
342      * @see #isDaemon
343      */
destroyIfEmptyDaemon()344     private void destroyIfEmptyDaemon() {
345         // Has to be non-destroyed daemon to make sense
346         synchronized (this.childrenThreadsLock) {
347             if (isDaemon && !isDestroyed && numThreads == 0) {
348                 synchronized (this.childrenGroupsLock) {
349                     if (numGroups == 0) {
350                         destroy();
351                     }
352                 }
353             }
354         }
355     }
356 
357     /**
358      * Iterates over all active threads in this group (and its sub-groups) and
359      * stores the threads in the given array. Returns when the array is full or
360      * no more threads remain, whichever happens first.
361      *
362      * @param threads the array into which the Threads will be copied
363      * @return the number of Threads that were copied
364      */
enumerate(Thread[] threads)365     public int enumerate(Thread[] threads) {
366         return enumerate(threads, true);
367     }
368 
369     /**
370      * Iterates over all active threads in this group (and, optionally, its
371      * sub-groups) and stores the threads in the given array. Returns when the
372      * array is full or no more threads remain, whichever happens first.
373      *
374      * @param threads the array into which the Threads will be copied
375      * @param recurse indicates whether Threads in subgroups should be
376      *        recursively copied as well
377      * @return the number of Threads that were copied
378      *
379      */
enumerate(Thread[] threads, boolean recurse)380     public int enumerate(Thread[] threads, boolean recurse) {
381         return enumerateGeneric(threads, recurse, 0, true);
382     }
383 
384     /**
385      * Iterates over all thread groups in this group (and its sub-groups) and
386      * and stores the groups in the given array. Returns when the array is full
387      * or no more groups remain, whichever happens first.
388      *
389      * @param groups the array into which the ThreadGroups will be copied
390      * @return the number of ThreadGroups that were copied
391      *
392      */
enumerate(ThreadGroup[] groups)393     public int enumerate(ThreadGroup[] groups) {
394         return enumerate(groups, true);
395     }
396 
397     /**
398      * Iterates over all thread groups in this group (and, optionally, its
399      * sub-groups) and and stores the groups in the given array. Returns when
400      * the array is full or no more groups remain, whichever happens first.
401      *
402      * @param groups the array into which the ThreadGroups will be copied
403      * @param recurse indicates whether ThreadGroups in subgroups should be
404      *        recursively copied as well or not
405      * @return the number of ThreadGroups that were copied
406      *
407      */
enumerate(ThreadGroup[] groups, boolean recurse)408     public int enumerate(ThreadGroup[] groups, boolean recurse) {
409         return enumerateGeneric(groups, recurse, 0, false);
410     }
411 
412     /**
413      * Copies into <param>enumeration</param> starting at
414      * <param>enumerationIndex</param> all Threads or ThreadGroups in the
415      * receiver. If <param>recurse</param> is true, recursively enumerate the
416      * elements in subgroups.
417      *
418      * If the array passed as parameter is too small no exception is thrown -
419      * the extra elements are simply not copied.
420      *
421      * @param enumeration array into which the elements will be copied
422      * @param recurse Indicates whether subgroups should be enumerated or not
423      * @param enumerationIndex Indicates in which position of the enumeration
424      *        array we are
425      * @param enumeratingThreads Indicates whether we are enumerating Threads or
426      *        ThreadGroups
427      * @return How many elements were enumerated/copied over
428      */
enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex, boolean enumeratingThreads)429     private int enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex,
430             boolean enumeratingThreads) {
431         checkAccess();
432 
433         Object[] immediateCollection = enumeratingThreads ? (Object[]) childrenThreads
434                 : (Object[]) childrenGroups;
435         Object syncLock = enumeratingThreads ? childrenThreadsLock : childrenGroupsLock;
436 
437         synchronized (syncLock) { // Lock this subpart of the tree as we walk
438             for (int i = enumeratingThreads ? numThreads : numGroups; --i >= 0;) {
439                 if (!enumeratingThreads || ((Thread) immediateCollection[i]).isAlive()) {
440                     if (enumerationIndex >= enumeration.length) {
441                         return enumerationIndex;
442                     }
443                     enumeration[enumerationIndex++] = immediateCollection[i];
444                 }
445             }
446         }
447 
448         if (recurse) { // Lock this subpart of the tree as we walk
449             synchronized (this.childrenGroupsLock) {
450                 for (int i = 0; i < numGroups; i++) {
451                     if (enumerationIndex >= enumeration.length) {
452                         return enumerationIndex;
453                     }
454                     enumerationIndex = childrenGroups[i].enumerateGeneric(enumeration, recurse,
455                             enumerationIndex, enumeratingThreads);
456                 }
457             }
458         }
459         return enumerationIndex;
460     }
461 
462     /**
463      * Returns the maximum allowed priority for a Thread in the receiver.
464      *
465      * @return the maximum priority
466      *
467      * @see #setMaxPriority
468      */
getMaxPriority()469     public final int getMaxPriority() {
470         return maxPriority;
471     }
472 
473     /**
474      * Returns the name of the receiver.
475      *
476      * @return the receiver's name
477      */
getName()478     public final String getName() {
479         return name;
480     }
481 
482     /**
483      * Returns the receiver's parent ThreadGroup. It can be {@code null}  if the
484      * receiver is the the root ThreadGroup.
485      *
486      * @return the parent ThreadGroup
487      *
488      */
getParent()489     public final ThreadGroup getParent() {
490         if (parent != null) {
491             parent.checkAccess();
492         }
493         return parent;
494     }
495 
496     /**
497      * Interrupts every Thread in the receiver and recursively in all its
498      * subgroups.
499      *
500      * @throws SecurityException if {@code this.checkAccess()} fails with
501      *         a SecurityException
502      *
503      * @see Thread#interrupt
504      */
interrupt()505     public final void interrupt() {
506         checkAccess();
507         // Lock this subpart of the tree as we walk
508         synchronized (this.childrenThreadsLock) {
509             for (int i = 0; i < numThreads; i++) {
510                 this.childrenThreads[i].interrupt();
511             }
512         }
513         // Lock this subpart of the tree as we walk
514         synchronized (this.childrenGroupsLock) {
515             for (int i = 0; i < numGroups; i++) {
516                 this.childrenGroups[i].interrupt();
517             }
518         }
519     }
520 
521     /**
522      * Checks whether the receiver is a daemon ThreadGroup.
523      *
524      * @return true if (and only if) the receiver is a daemon ThreadGroup
525      *
526      * @see #setDaemon
527      * @see #destroy
528      */
isDaemon()529     public final boolean isDaemon() {
530         return isDaemon;
531     }
532 
533     /**
534      * Checks whether the receiver has already been destroyed.
535      *
536      * @return true if (and only if) the receiver has already been destroyed
537      *
538      * @see #destroy
539      */
isDestroyed()540     public synchronized boolean isDestroyed() {
541         return isDestroyed;
542     }
543 
544     /**
545      * Outputs to {@code System.out} a text representation of the
546      * hierarchy of Threads and ThreadGroups in the receiver (and recursively).
547      * Proper indentation is done to suggest the nesting of groups inside groups
548      * and threads inside groups.
549      */
list()550     public void list() {
551         // We start in a fresh line
552         System.out.println();
553         list(0);
554     }
555 
556     /*
557      * Outputs to {@code System.out}a text representation of the
558      * hierarchy of Threads and ThreadGroups in the receiver (and recursively).
559      * The indentation will be four spaces per level of nesting.
560      *
561      * @param levels How many levels of nesting, so that proper indentation can
562      * be output.
563      */
list(int levels)564     private void list(int levels) {
565         for (int i = 0; i < levels; i++) {
566             System.out.print("    "); // 4 spaces for each level
567         }
568 
569         // Print the receiver
570         System.out.println(this.toString());
571 
572         // Print the children threads, with 1 extra indentation
573         synchronized (this.childrenThreadsLock) {
574             for (int i = 0; i < numThreads; i++) {
575                 // children get an extra indentation, 4 spaces for each level
576                 for (int j = 0; j <= levels; j++) {
577                     System.out.print("    ");
578                 }
579                 System.out.println(this.childrenThreads[i]);
580             }
581         }
582         synchronized (this.childrenGroupsLock) {
583             for (int i = 0; i < numGroups; i++) {
584                 this.childrenGroups[i].list(levels + 1);
585             }
586         }
587     }
588 
589     /**
590      * Checks whether the receiver is a direct or indirect parent group of a
591      * given ThreadGroup.
592      *
593      * @param g the potential child ThreadGroup
594      *
595      * @return true if (and only if) the receiver is parent of {@code g}
596      *
597      */
parentOf(ThreadGroup g)598     public final boolean parentOf(ThreadGroup g) {
599         while (g != null) {
600             if (this == g) {
601                 return true;
602             }
603             g = g.parent;
604         }
605         return false;
606     }
607 
608     /**
609      * Removes a Thread from the receiver. This should only be visible to class
610      * java.lang.Thread, and should only be called when a Thread dies.
611      *
612      * @param thread Thread to remove from the receiver
613      *
614      * @see #add(Thread)
615      */
remove(java.lang.Thread thread)616     final void remove(java.lang.Thread thread) {
617         synchronized (this.childrenThreadsLock) {
618             for (int i = 0; i < numThreads; i++) {
619                 if (childrenThreads[i].equals(thread)) {
620                     numThreads--;
621                     System
622                             .arraycopy(childrenThreads, i + 1, childrenThreads, i, numThreads
623                                     - i);
624                     childrenThreads[numThreads] = null;
625                     break;
626                 }
627             }
628         }
629         destroyIfEmptyDaemon();
630     }
631 
632     /**
633      * Removes an immediate subgroup from the receiver.
634      *
635      * @param g ThreadGroup to remove from the receiver
636      *
637      * @see #add(Thread)
638      * @see #add(ThreadGroup)
639      */
remove(ThreadGroup g)640     private void remove(ThreadGroup g) {
641         synchronized (this.childrenGroupsLock) {
642             for (int i = 0; i < numGroups; i++) {
643                 if (childrenGroups[i].equals(g)) {
644                     numGroups--;
645                     System.arraycopy(childrenGroups, i + 1, childrenGroups, i, numGroups - i);
646                     childrenGroups[numGroups] = null;
647                     break;
648                 }
649             }
650         }
651         destroyIfEmptyDaemon();
652     }
653 
654     /**
655      * Resumes every Thread in the receiver and recursively in all its
656      * subgroups.
657      *
658      * @throws SecurityException if {@code this.checkAccess()} fails with
659      *         a SecurityException
660      *
661      * @see Thread#resume
662      * @see #suspend
663      *
664      * @deprecated Requires deprecated method Thread.resume().
665      */
666     @SuppressWarnings("deprecation")
667     @Deprecated
resume()668     public final void resume() {
669         checkAccess();
670         // Lock this subpart of the tree as we walk
671         synchronized (this.childrenThreadsLock) {
672             for (int i = 0; i < numThreads; i++) {
673                 this.childrenThreads[i].resume();
674             }
675         }
676         // Lock this subpart of the tree as we walk
677         synchronized (this.childrenGroupsLock) {
678             for (int i = 0; i < numGroups; i++) {
679                 this.childrenGroups[i].resume();
680             }
681         }
682     }
683 
684     /**
685      * Configures the receiver to be a daemon ThreadGroup or not. Daemon
686      * ThreadGroups are automatically destroyed when they become empty.
687      *
688      * @param isDaemon the new value defining if receiver should be daemon or
689      *                 not
690      *
691      * @throws SecurityException if {@code checkAccess()} for the parent
692      *         group fails with a SecurityException
693      *
694      * @see #isDaemon
695      * @see #destroy
696      */
setDaemon(boolean isDaemon)697     public final void setDaemon(boolean isDaemon) {
698         checkAccess();
699         this.isDaemon = isDaemon;
700     }
701 
702     /**
703      * Configures the maximum allowed priority for a Thread in the receiver and
704      * recursively in all its subgroups.
705      *
706      * One can never change the maximum priority of a ThreadGroup to be higher
707      * than it was. Such an attempt will not result in an exception, it will
708      * simply leave the ThreadGroup with its current maximum priority.
709      *
710      * @param newMax the new maximum priority to be set
711      *
712      * @throws SecurityException if {@code checkAccess()} fails with a
713      *         SecurityException
714      * @throws IllegalArgumentException if the new priority is greater than
715      *         Thread.MAX_PRIORITY or less than Thread.MIN_PRIORITY
716      *
717      * @see #getMaxPriority
718      */
setMaxPriority(int newMax)719     public final void setMaxPriority(int newMax) {
720         checkAccess();
721 
722         if (newMax <= this.maxPriority) {
723             if (newMax < Thread.MIN_PRIORITY) {
724                 newMax = Thread.MIN_PRIORITY;
725             }
726 
727             int parentPriority = parent == null ? newMax : parent.getMaxPriority();
728             this.maxPriority = parentPriority <= newMax ? parentPriority : newMax;
729             // Lock this subpart of the tree as we walk
730             synchronized (this.childrenGroupsLock) {
731                 // ??? why not maxPriority
732                 for (int i = 0; i < numGroups; i++) {
733                     this.childrenGroups[i].setMaxPriority(newMax);
734                 }
735             }
736         }
737     }
738 
739     /**
740      * Sets the parent ThreadGroup of the receiver, and adds the receiver to the
741      * parent's collection of immediate children (if {@code parent} is
742      * not {@code null}).
743      *
744      * @param parent The parent ThreadGroup, or null if the receiver is to be
745      *        the root ThreadGroup
746      *
747      * @see #getParent
748      * @see #parentOf
749      */
setParent(ThreadGroup parent)750     private void setParent(ThreadGroup parent) {
751         if (parent != null) {
752             parent.add(this);
753         }
754         this.parent = parent;
755     }
756 
757     /**
758      * Stops every Thread in the receiver and recursively in all its subgroups.
759      *
760      * @throws SecurityException if {@code this.checkAccess()} fails with
761      *         a SecurityException
762      *
763      * @see Thread#stop()
764      * @see Thread#stop(Throwable)
765      * @see ThreadDeath
766      *
767      * @deprecated Requires deprecated method Thread.stop().
768      */
769     @SuppressWarnings("deprecation")
770     @Deprecated
stop()771     public final void stop() {
772         if (stopHelper()) {
773             Thread.currentThread().stop();
774         }
775     }
776 
777     /**
778      * @deprecated Requires deprecated method Thread.suspend().
779      */
780     @SuppressWarnings("deprecation")
781     @Deprecated
stopHelper()782     private final boolean stopHelper() {
783         checkAccess();
784 
785         boolean stopCurrent = false;
786         // Lock this subpart of the tree as we walk
787         synchronized (this.childrenThreadsLock) {
788             Thread current = Thread.currentThread();
789             for (int i = 0; i < numThreads; i++) {
790                 if (this.childrenThreads[i] == current) {
791                     stopCurrent = true;
792                 } else {
793                     this.childrenThreads[i].stop();
794                 }
795             }
796         }
797         // Lock this subpart of the tree as we walk
798         synchronized (this.childrenGroupsLock) {
799             for (int i = 0; i < numGroups; i++) {
800                 stopCurrent |= this.childrenGroups[i].stopHelper();
801             }
802         }
803         return stopCurrent;
804     }
805 
806     /**
807      * Suspends every Thread in the receiver and recursively in all its
808      * subgroups.
809      *
810      * @throws SecurityException if {@code this.checkAccess()} fails with
811      *         a SecurityException
812      *
813      * @see Thread#suspend
814      * @see #resume
815      *
816      * @deprecated Requires deprecated method Thread.suspend().
817      */
818     @SuppressWarnings("deprecation")
819     @Deprecated
suspend()820     public final void suspend() {
821         if (suspendHelper()) {
822             Thread.currentThread().suspend();
823         }
824     }
825 
826     /**
827      * @deprecated Requires deprecated method Thread.suspend().
828      */
829     @SuppressWarnings("deprecation")
830     @Deprecated
suspendHelper()831     private final boolean suspendHelper() {
832         checkAccess();
833 
834         boolean suspendCurrent = false;
835         // Lock this subpart of the tree as we walk
836         synchronized (this.childrenThreadsLock) {
837             Thread current = Thread.currentThread();
838             for (int i = 0; i < numThreads; i++) {
839                 if (this.childrenThreads[i] == current) {
840                     suspendCurrent = true;
841                 } else {
842                     this.childrenThreads[i].suspend();
843                 }
844             }
845         }
846         // Lock this subpart of the tree as we walk
847         synchronized (this.childrenGroupsLock) {
848             for (int i = 0; i < numGroups; i++) {
849                 suspendCurrent |= this.childrenGroups[i].suspendHelper();
850             }
851         }
852         return suspendCurrent;
853     }
854 
855     /**
856      * Returns a string containing a concise, human-readable description of the
857      * receiver.
858      *
859      * @return a printable representation of the ThreadGroup
860      */
861     @Override
toString()862     public String toString() {
863         return getClass().getName() + "[name=" + this.getName() + ",maxpri="
864                 + this.getMaxPriority() + "]";
865     }
866 
867     /**
868      * Handles uncaught exceptions. Any uncaught exception in any Thread
869      * is forwarded (by the VM) to the Thread's ThreadGroup by sending this
870      * message (uncaughtException). This allows users to define custom
871      * ThreadGroup classes and custom behavior for when a Thread has an
872      * uncaughtException or when it does (ThreadDeath).
873      *
874      * @param t the Thread that terminated with an uncaught exception
875      * @param e the uncaught exception itself
876      *
877      * @see Thread#stop()
878      * @see Thread#stop(Throwable)
879      * @see ThreadDeath
880      */
uncaughtException(Thread t, Throwable e)881     public void uncaughtException(Thread t, Throwable e) {
882         // BEGIN android-changed
883         if (parent != null) {
884             parent.uncaughtException(t, e);
885         } else if (Thread.getDefaultUncaughtExceptionHandler() != null) {
886             // TODO The spec is unclear regarding this. What do we do?
887             Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, e);
888         } else if (!(e instanceof ThreadDeath)) {
889             // No parent group, has to be 'system' Thread Group
890             e.printStackTrace(System.err);
891         }
892         // END android-changed
893     }
894 
895     // BEGIN android-added
896     /**
897      * Non-standard method for adding a thread to a group, required by Dalvik.
898      *
899      * @param thread Thread to add to the receiver
900      *
901      * @throws IllegalThreadStateException if the receiver has been destroyed
902      *         already
903      *
904      * @see #add(java.lang.Thread)
905      * @see #removeThread(java.lang.Thread)
906      */
addThread(Thread thread)907     void addThread(Thread thread) throws IllegalThreadStateException {
908         add(thread);
909     }
910 
911     /**
912      * Non-standard method for adding a thread to a group, required by Dalvik.
913      *
914      * @param thread Thread to add to the receiver
915      *
916      * @throws IllegalThreadStateException if the receiver has been destroyed
917      *         already
918      *
919      * @see #remove(java.lang.Thread)
920      * @see #addThread(java.lang.Thread)
921      */
removeThread(Thread thread)922     void removeThread(Thread thread) throws IllegalThreadStateException {
923         remove(thread);
924     }
925     // END android-added
926 }
927