1554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe/*
2554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe * Copyright (C) 2015 The Android Open Source Project
3554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe *
4554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe * Licensed under the Apache License, Version 2.0 (the "License");
5554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe * you may not use this file except in compliance with the License.
6554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe * You may obtain a copy of the License at
7554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe *
8554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe *      http://www.apache.org/licenses/LICENSE-2.0
9554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe *
10554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe * Unless required by applicable law or agreed to in writing, software
11554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe * distributed under the License is distributed on an "AS IS" BASIS,
12554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe * See the License for the specific language governing permissions and
14554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe * limitations under the License.
15554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe */
16554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
17554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampepackage com.android.preload;
18554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
19554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampeimport com.android.ddmlib.AndroidDebugBridge;
20554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampeimport com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
21554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampeimport com.android.ddmlib.Client;
22554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampeimport com.android.ddmlib.IDevice;
23554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
24554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe/**
25554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe * Helper class for common communication with a Client (the ddms name for a running application).
26554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe *
27554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe * Instances take a default timeout parameter that's applied to all functions without explicit
28554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe * timeout. Timeouts are in milliseconds.
29554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe */
30554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampepublic class ClientUtils {
31554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
32554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    private int defaultTimeout;
33554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
34554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    public ClientUtils() {
35554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        this(10000);
36554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    }
37554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
38554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    public ClientUtils(int defaultTimeout) {
39554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        this.defaultTimeout = defaultTimeout;
40554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    }
41554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
42554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    /**
43554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     * Shortcut for findClient with default timeout.
44554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     */
45554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    public Client findClient(IDevice device, String processName, int processPid) {
46554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        return findClient(device, processName, processPid, defaultTimeout);
47554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    }
48554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
49554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    /**
50554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     * Find the client with the given process name or process id. The name takes precedence over
51554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     * the process id (if valid). Stop looking after the given timeout.
52554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     *
53554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     * @param device The device to communicate with.
54554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     * @param processName The name of the process. May be null.
55554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     * @param processPid The pid of the process. Values less than or equal to zero are ignored.
56554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     * @param timeout The amount of milliseconds to wait, at most.
57554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     * @return The client, if found. Otherwise null.
58554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     */
59554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    public Client findClient(IDevice device, String processName, int processPid, int timeout) {
60554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        WaitForClient wfc = new WaitForClient(device, processName, processPid, timeout);
61554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        return wfc.get();
62554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    }
63554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
64554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    /**
65554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     * Shortcut for findAllClients with default timeout.
66554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     */
67554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    public Client[] findAllClients(IDevice device) {
68554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        return findAllClients(device, defaultTimeout);
69554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    }
70554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
71554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    /**
72554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     * Retrieve all clients known to the given device. Wait at most the given timeout.
73554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     *
74554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     * @param device The device to investigate.
75554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     * @param timeout The amount of milliseconds to wait, at most.
76554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     * @return An array of clients running on the given device. May be null depending on the
77554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     *         device implementation.
78554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe     */
79554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    public Client[] findAllClients(IDevice device, int timeout) {
80554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        if (device.hasClients()) {
81554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            return device.getClients();
82554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        }
83554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        WaitForClients wfc = new WaitForClients(device, timeout);
84554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        return wfc.get();
85554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    }
86554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
87554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    private static class WaitForClient implements IClientChangeListener {
88554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
89554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        private IDevice device;
90554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        private String processName;
91554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        private int processPid;
92554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        private long timeout;
93554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        private Client result;
94554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
95554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        public WaitForClient(IDevice device, String processName, int processPid, long timeout) {
96554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            this.device = device;
97554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            this.processName = processName;
98554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            this.processPid = processPid;
99554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            this.timeout = timeout;
100554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            this.result = null;
101554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        }
102554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
103554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        public Client get() {
104554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            synchronized (this) {
105554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                AndroidDebugBridge.addClientChangeListener(this);
106554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
107554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                // Maybe it's already there.
108554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                if (result == null) {
109554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                    result = searchForClient(device);
110554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                }
111554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
112554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                if (result == null) {
113554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                    try {
114554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                        wait(timeout);
115554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                    } catch (InterruptedException e) {
116554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                        // Note: doesn't guard for spurious wakeup.
117554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                    }
118554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                }
119554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            }
120554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
121554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            AndroidDebugBridge.removeClientChangeListener(this);
122554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            return result;
123554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        }
124554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
125554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        private Client searchForClient(IDevice device) {
126554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            if (processName != null) {
127554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                Client tmp = device.getClient(processName);
128554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                if (tmp != null) {
129554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                    return tmp;
130554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                }
131554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            }
132554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            if (processPid > 0) {
133554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                String name = device.getClientName(processPid);
134554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                if (name != null && !name.isEmpty()) {
135554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                    Client tmp = device.getClient(name);
136554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                    if (tmp != null) {
137554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                        return tmp;
138554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                    }
139554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                }
140554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            }
141554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            if (processPid > 0) {
142554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                // Try manual search.
143554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                for (Client cl : device.getClients()) {
144554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                    if (cl.getClientData().getPid() == processPid
145554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                            && cl.getClientData().getClientDescription() != null) {
146554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                        return cl;
147554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                    }
148554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                }
149554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            }
150554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            return null;
151554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        }
152554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
153554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        private boolean isTargetClient(Client c) {
154554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            if (processPid > 0 && c.getClientData().getPid() == processPid) {
155554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                return true;
156554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            }
157554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            if (processName != null
158554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                    && processName.equals(c.getClientData().getClientDescription())) {
159554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                return true;
160554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            }
161554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            return false;
162554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        }
163554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
164554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        @Override
165554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        public void clientChanged(Client arg0, int arg1) {
166554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            synchronized (this) {
167554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
168554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                    if (isTargetClient(arg0)) {
169554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                        result = arg0;
170554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                        notifyAll();
171554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                    }
172554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                }
173554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            }
174554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        }
175554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    }
176554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
177554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    private static class WaitForClients implements IClientChangeListener {
178554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
179554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        private IDevice device;
180554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        private long timeout;
181554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
182554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        public WaitForClients(IDevice device, long timeout) {
183554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            this.device = device;
184554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            this.timeout = timeout;
185554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        }
186554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
187554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        public Client[] get() {
188554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            synchronized (this) {
189554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                AndroidDebugBridge.addClientChangeListener(this);
190554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
191554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                if (device.hasClients()) {
192554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                    return device.getClients();
193554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                }
194554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
195554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                try {
196554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                    wait(timeout); // Note: doesn't guard for spurious wakeup.
197554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                } catch (InterruptedException exc) {
198554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                }
199554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
200554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                // We will be woken up when the first client data arrives. Sleep a little longer
201554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                // to give (hopefully all of) the rest of the clients a chance to become available.
202554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                // Note: a loop with timeout is brittle as well and complicated, just accept this
203554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                //       for now.
204554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                try {
205554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                    Thread.sleep(500);
206554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                } catch (InterruptedException exc) {
207554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                }
208554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            }
209554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
210554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            AndroidDebugBridge.removeClientChangeListener(this);
211554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
212554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            return device.getClients();
213554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        }
214554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe
215554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        @Override
216554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        public void clientChanged(Client arg0, int arg1) {
217554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            synchronized (this) {
218554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
219554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                    notifyAll();
220554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe                }
221554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe            }
222554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe        }
223554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe    }
224554d7ee0f5d177b6c0bce805f5a5917b6b211978Andreas Gampe}
225