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  */
  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  */
  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
 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 now way to return parameters (especially big ones)
 192  *               Use sendMemoryPackage() to do that!
 193  */
 194 LRESULT
 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("In AccessBridgeVMInstance::sendPackage");
 202     PrintDebugString("    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
 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("    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("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("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 }