1 /*
2  * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 /*
27  * A class to track key JVM instance info from the AT WinAccessBridge
28  */
29 
30 #include "AccessBridgeDebug.h"
31 #include "AccessBridgeJavaVMInstance.h"
32 #include "AccessBridgeMessages.h"
33 #include "AccessBridgePackages.h"
34 #include "accessBridgeResource.h"       // for debugging messages
35 
36 #include <winbase.h>
37 #include <jni.h>
38 
39 // The initialization must only be done one time and to provide for that the initialization
40 // is now done in WinAccessBridge and the CRITICAL_SECTION memory has been moved to there.
41 // send memory lock
42 //CRITICAL_SECTION sendMemoryIPCLock;
43 extern CRITICAL_SECTION sendMemoryIPCLock;
44 
45 // protects the javaVMs chain while in use
46 extern bool isVMInstanceChainInUse;
47 
48 DEBUG_CODE(extern HWND theDialogWindow);
49 extern "C" {
50     DEBUG_CODE(void AppendToCallInfo(char *s));
51 }
52 
53 
54 /**
55  *
56  *
57  */
AccessBridgeJavaVMInstance(HWND ourABWindow,HWND javaABWindow,long javaVMID,AccessBridgeJavaVMInstance * next)58 AccessBridgeJavaVMInstance::AccessBridgeJavaVMInstance(HWND ourABWindow,
59                                                        HWND javaABWindow,
60                                                        long javaVMID,
61                                                        AccessBridgeJavaVMInstance *next) {
62     goingAway = FALSE;
63     // This should be called once.  Moved to WinAccessBridge c'tor
64     //InitializeCriticalSection(&sendMemoryIPCLock);
65     ourAccessBridgeWindow = ourABWindow;
66     javaAccessBridgeWindow = javaABWindow;
67     vmID = javaVMID;
68     nextJVMInstance = next;
69     memoryMappedFileMapHandle = (HANDLE) 0;
70     memoryMappedView = (char *) 0;
71     sprintf(memoryMappedFileName, "AccessBridge-%p-%p.mmf",
72             ourAccessBridgeWindow, javaAccessBridgeWindow);
73 }
74 
75 /**
76  *
77  *
78  */
~AccessBridgeJavaVMInstance()79 AccessBridgeJavaVMInstance::~AccessBridgeJavaVMInstance() {
80     DEBUG_CODE(char buffer[256]);
81 
82     DEBUG_CODE(AppendToCallInfo("***** in AccessBridgeJavaVMInstance::~AccessBridgeJavaVMInstance\r\n"));
83     EnterCriticalSection(&sendMemoryIPCLock);
84 
85     // if IPC memory mapped file view is valid, unmap it
86     goingAway = TRUE;
87     if (memoryMappedView != (char *) 0) {
88         DEBUG_CODE(sprintf(buffer, "  unmapping memoryMappedView; view = %p\r\n", memoryMappedView));
89         DEBUG_CODE(AppendToCallInfo(buffer));
90         UnmapViewOfFile(memoryMappedView);
91         memoryMappedView = (char *) 0;
92     }
93     // if IPC memory mapped file handle map is open, close it
94     if (memoryMappedFileMapHandle != (HANDLE) 0) {
95         DEBUG_CODE(sprintf(buffer, "  closing memoryMappedFileMapHandle; handle = %p\r\n", memoryMappedFileMapHandle));
96         DEBUG_CODE(AppendToCallInfo(buffer));
97         CloseHandle(memoryMappedFileMapHandle);
98         memoryMappedFileMapHandle = (HANDLE) 0;
99     }
100     LeaveCriticalSection(&sendMemoryIPCLock);
101 
102 }
103 
104 /**
105  * initiateIPC - sets up the memory-mapped file to do IPC messaging
106  *               1 file is created: to handle requests for information
107  *               initiated from Windows AT.  The package is placed into
108  *               the memory-mapped file (char *memoryMappedView),
109  *               and then a special SendMessage() is sent.  When the
110  *               JavaDLL returns from SendMessage() processing, the
111  *               data will be in memoryMappedView.  The SendMessage()
112  *               return value tells us if all is right with the world.
113  *
114  *               The set-up proces involves creating the memory-mapped
115  *               file, and handshaking with the JavaDLL so it knows
116  *               about it as well.
117  *
118  */
119 LRESULT
initiateIPC()120 AccessBridgeJavaVMInstance::initiateIPC() {
121     DEBUG_CODE(char debugBuf[256]);
122     DWORD errorCode;
123 
124     DEBUG_CODE(AppendToCallInfo(" in AccessBridgeJavaVMInstance::initiateIPC()\r\n"));
125 
126     // create Windows-initiated IPC file & map it to a ptr
127     memoryMappedFileMapHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
128                                                   PAGE_READWRITE, 0,
129                                                   // 8 bytes for return code
130                                                   sizeof(WindowsInitiatedPackages) + 8,
131                                                   memoryMappedFileName);
132     if (memoryMappedFileMapHandle == NULL) {
133         errorCode = GetLastError();
134         DEBUG_CODE(sprintf(debugBuf, "  Failed to CreateFileMapping for %s, error: %X", memoryMappedFileName, errorCode));
135         DEBUG_CODE(AppendToCallInfo(debugBuf));
136         return errorCode;
137     } else {
138         DEBUG_CODE(sprintf(debugBuf, "  CreateFileMapping worked - filename: %s\r\n", memoryMappedFileName));
139         DEBUG_CODE(AppendToCallInfo(debugBuf));
140     }
141 
142     memoryMappedView = (char *) MapViewOfFile(memoryMappedFileMapHandle,
143                                               FILE_MAP_READ | FILE_MAP_WRITE,
144                                               0, 0, 0);
145     if (memoryMappedView == NULL) {
146         errorCode = GetLastError();
147         DEBUG_CODE(sprintf(debugBuf, "  Failed to MapViewOfFile for %s, error: %X", memoryMappedFileName, errorCode));
148         DEBUG_CODE(AppendToCallInfo(debugBuf));
149         return errorCode;
150     } else {
151         DEBUG_CODE(sprintf(debugBuf, "  MapViewOfFile worked - view: %p\r\n", memoryMappedView));
152         DEBUG_CODE(AppendToCallInfo(debugBuf));
153     }
154 
155 
156     // write some data to the memory mapped file
157     strcpy(memoryMappedView, AB_MEMORY_MAPPED_FILE_OK_QUERY);
158 
159 
160     // inform the JavaDLL that we've a memory mapped file ready for it
161     char buffer[sizeof(PackageType) + sizeof(MemoryMappedFileCreatedPackage)];
162     PackageType *type = (PackageType *) buffer;
163     MemoryMappedFileCreatedPackage *pkg = (MemoryMappedFileCreatedPackage *) (buffer + sizeof(PackageType));
164     *type = cMemoryMappedFileCreatedPackage;
165     pkg->bridgeWindow = ABHandleToLong(ourAccessBridgeWindow);
166     strncpy(pkg->filename, memoryMappedFileName, cMemoryMappedNameSize);
167     sendPackage(buffer, sizeof(buffer));
168 
169 
170     // look for the JavaDLL's answer to see if it could read the file
171     if (strcmp(memoryMappedView, AB_MEMORY_MAPPED_FILE_OK_ANSWER) != 0) {
172         DEBUG_CODE(sprintf(debugBuf, "  JavaVM failed to deal with memory mapped file %s\r\n",
173                       memoryMappedFileName));
174         DEBUG_CODE(AppendToCallInfo(debugBuf));
175         return -1;
176     } else {
177         DEBUG_CODE(sprintf(debugBuf, "  Success!  JavaVM accpeted our file\r\n"));
178         DEBUG_CODE(AppendToCallInfo(debugBuf));
179     }
180 
181     return 0;
182 }
183 
184 // -----------------------
185 
186 /**
187  * sendPackage - uses SendMessage(WM_COPYDATA) to do IPC messaging
188  *               with the Java AccessBridge DLL
189  *
190  *               NOTE: WM_COPYDATA is only for one-way IPC; there
191  *               is no way to return parameters (especially big ones)
192  *               Use sendMemoryPackage() to do that!
193  */
194 LRESULT
sendPackage(char * buffer,long bufsize)195 AccessBridgeJavaVMInstance::sendPackage(char *buffer, long bufsize) {
196     COPYDATASTRUCT toCopy;
197     toCopy.dwData = 0;          // 32-bits we could use for something...
198     toCopy.cbData = bufsize;
199     toCopy.lpData = buffer;
200 
201     PrintDebugString("[INFO]: In AccessBridgeVMInstance::sendPackage");
202     PrintDebugString("[INFO]:     javaAccessBridgeWindow: %p", javaAccessBridgeWindow);
203     /* This was SendMessage.  Normally that is a blocking call.  However, if
204      * SendMessage is sent to another process, e.g. another JVM and an incoming
205      * SendMessage is pending, control will be passed to the DialogProc to handle
206      * the incoming message.  A bug occurred where this allowed an AB_DLL_GOING_AWAY
207      * message to be processed deleting an AccessBridgeJavaVMInstance object in
208      * the javaVMs chain.  SendMessageTimeout with SMTO_BLOCK set will prevent the
209      * calling thread from processing other requests while waiting, i.e control
210      * will not be passed to the DialogProc.  Also note that PostMessage or
211      * SendNotifyMessage can't be used.  Although they don't allow transfer to
212      * the DialogProc they can't be used in cases where pointers are passed.  This
213      * is because the referenced memory needs to be available when the other thread
214      * gets control.
215      */
216     UINT flags = SMTO_BLOCK | SMTO_NOTIMEOUTIFNOTHUNG;
217     DWORD_PTR out; // not used
218     LRESULT lr = SendMessageTimeout( javaAccessBridgeWindow, WM_COPYDATA,
219                                      (WPARAM)ourAccessBridgeWindow, (LPARAM)&toCopy,
220                                      flags, 4000, &out );
221     return lr;
222 }
223 
224 
225 /**
226  * sendMemoryPackage - uses Memory-Mapped files to do IPC messaging
227  *                     with the Java AccessBridge DLL, informing the
228  *                     Java AccessBridge DLL via SendMessage that something
229  *                     is waiting for it in the shared file...
230  *
231  *                     In the SendMessage call, the third param (WPARAM) is
232  *                     the source HWND (ourAccessBridgeWindow in this case),
233  *                     and the fourth param (LPARAM) is the size in bytes of
234  *                     the package put into shared memory.
235  *
236  */
237 BOOL
sendMemoryPackage(char * buffer,long bufsize)238 AccessBridgeJavaVMInstance::sendMemoryPackage(char *buffer, long bufsize) {
239 
240     // Protect against race condition where the memory mapped file is
241     // deallocated before the memory package is being sent
242     if (goingAway) {
243         return FALSE;
244     }
245     BOOL retval = FALSE;
246 
247     DEBUG_CODE(char outputBuf[256]);
248     DEBUG_CODE(sprintf(outputBuf, "AccessBridgeJavaVMInstance::sendMemoryPackage(, %d)", bufsize));
249     DEBUG_CODE(AppendToCallInfo(outputBuf));
250 
251     DEBUG_CODE(PackageType *type = (PackageType *) buffer);
252     DEBUG_CODE(if (*type == cGetAccessibleTextRangePackage) {)
253         DEBUG_CODE(AppendToCallInfo("  'buffer' contains:"));
254         DEBUG_CODE(GetAccessibleTextRangePackage *pkg = (GetAccessibleTextRangePackage *) (buffer + sizeof(PackageType)));
255         DEBUG_CODE(sprintf(outputBuf, "    PackageType = %X", *type));
256         DEBUG_CODE(AppendToCallInfo(outputBuf));
257         DEBUG_CODE(sprintf(outputBuf, "    GetAccessibleTextRange: start = %d, end = %d, rText = %ls",
258             pkg->start, pkg->end, pkg->rText));
259         DEBUG_CODE(AppendToCallInfo(outputBuf));
260     DEBUG_CODE(})
261 
262     EnterCriticalSection(&sendMemoryIPCLock);
263     {
264         // copy the package into shared memory
265         if (!goingAway) {
266             memcpy(memoryMappedView, buffer, bufsize);
267 
268             DEBUG_CODE(PackageType *type = (PackageType *) memoryMappedView);
269             DEBUG_CODE(if (*type == cGetAccessibleTextItemsPackage) {)
270                 DEBUG_CODE(AppendToCallInfo("  'memoryMappedView' now contains:"));
271                 DEBUG_CODE(GetAccessibleTextItemsPackage *pkg = (GetAccessibleTextItemsPackage *) (buffer + sizeof(PackageType)));
272                 DEBUG_CODE(sprintf(outputBuf, "    PackageType = %X", *type));
273                 DEBUG_CODE(AppendToCallInfo(outputBuf));
274             DEBUG_CODE(})
275         }
276 
277         if (!goingAway) {
278             // Let the recipient know there is a package waiting for them. The unset byte
279             // at end of buffer which will only be set if message is properly received
280             char *done = &memoryMappedView[bufsize];
281             *done = 0;
282 
283             PrintDebugString("[INFO]:     javaAccessBridgeWindow: %p", javaAccessBridgeWindow);
284             // See the comment above the call to SendMessageTimeout in SendPackage method above.
285             UINT flags = SMTO_BLOCK | SMTO_NOTIMEOUTIFNOTHUNG;
286             DWORD_PTR out; // not used
287             SendMessageTimeout( javaAccessBridgeWindow, AB_MESSAGE_WAITING, (WPARAM)ourAccessBridgeWindow, (LPARAM)bufsize,
288                                 flags, 4000, &out );
289 
290             // only succeed if message has been properly received
291             if(!goingAway) retval = (*done == 1);
292         }
293 
294         // copy the package back from shared memory
295         if (!goingAway) {
296             memcpy(buffer, memoryMappedView, bufsize);
297         }
298     }
299     LeaveCriticalSection(&sendMemoryIPCLock);
300     return retval;
301 }
302 
303 
304 /**
305  * findAccessBridgeWindow - walk through linked list from where we are,
306  *                          return the HWND of the ABJavaVMInstance that
307  *                          matches the passed in vmID; no match: return 0
308  *
309  */
310 HWND
311 AccessBridgeJavaVMInstance::findAccessBridgeWindow(long javaVMID) {
312     PrintDebugString("[INFO]: In findAccessBridgeWindow");
313     // no need to recurse really
314     if (vmID == javaVMID) {
315         return javaAccessBridgeWindow;
316     } else {
317         isVMInstanceChainInUse = true;
318         AccessBridgeJavaVMInstance *current = nextJVMInstance;
319         while (current != (AccessBridgeJavaVMInstance *) 0) {
320             if (current->vmID == javaVMID) {
321                 isVMInstanceChainInUse = false;
322                 return current->javaAccessBridgeWindow;
323             }
324             current = current->nextJVMInstance;
325         }
326         isVMInstanceChainInUse = false;
327     }
328     return 0;
329 }
330 
331 /**
332  * findABJavaVMInstanceFromJavaHWND - walk through linked list from
333  *                                    where we are.  Return the
334  *                                    AccessBridgeJavaVMInstance
335  *                                    of the ABJavaVMInstance that
336  *                                    matches the passed in vmID;
337  *                                    no match: return 0
338  */
339 AccessBridgeJavaVMInstance *
340 AccessBridgeJavaVMInstance::findABJavaVMInstanceFromJavaHWND(HWND window) {
341     PrintDebugString("[INFO]: In findABJavaInstanceFromJavaHWND");
342     // no need to recurse really
343     if (javaAccessBridgeWindow == window) {
344         return this;
345     } else {
346         isVMInstanceChainInUse = true;
347         AccessBridgeJavaVMInstance *current = nextJVMInstance;
348         while (current != (AccessBridgeJavaVMInstance *) 0) {
349             if (current->javaAccessBridgeWindow == window) {
350                 isVMInstanceChainInUse = false;
351                 return current;
352             }
353             current = current->nextJVMInstance;
354         }
355     }
356     isVMInstanceChainInUse = false;
357     return (AccessBridgeJavaVMInstance *) 0;
358 }
359