OSDN Git Service

CMFM: CYAN-533 - Increase timeout in File Manager
authorJorge Ruesga <jorge@ruesga.com>
Sat, 9 Mar 2013 23:13:19 +0000 (00:13 +0100)
committerJorge Ruesga <jorge@ruesga.com>
Sat, 9 Mar 2013 23:13:19 +0000 (00:13 +0100)
Fixed timeout problems with list, copy, move and delete commands.
Check new data for list commands; use indefinitely wait for copy, move and delete commands.
Tested with 5000 and 30000 files.

Change-Id: I33cd6c9b7422966cdc4bc0c9cb265f74533ef161
JIRA: https://jira.cyanogenmod.org/browse/CYAN-533
Bugfix: CYAN-533
Signed-off-by: Jorge Ruesga <jorge@ruesga.com>
16 files changed:
res/xml/command_list.xml
src/com/cyanogenmod/filemanager/commands/ExecutableCreator.java
src/com/cyanogenmod/filemanager/commands/ProcessIdExecutable.java
src/com/cyanogenmod/filemanager/commands/java/JavaExecutableCreator.java
src/com/cyanogenmod/filemanager/commands/shell/AsyncResultProgram.java
src/com/cyanogenmod/filemanager/commands/shell/CopyCommand.java
src/com/cyanogenmod/filemanager/commands/shell/DeleteDirCommand.java
src/com/cyanogenmod/filemanager/commands/shell/DeleteFileCommand.java
src/com/cyanogenmod/filemanager/commands/shell/ListCommand.java
src/com/cyanogenmod/filemanager/commands/shell/MoveCommand.java
src/com/cyanogenmod/filemanager/commands/shell/ProcessIdCommand.java
src/com/cyanogenmod/filemanager/commands/shell/Program.java
src/com/cyanogenmod/filemanager/commands/shell/QuickFolderSearchCommand.java
src/com/cyanogenmod/filemanager/commands/shell/ShellExecutableCreator.java
src/com/cyanogenmod/filemanager/console/shell/ShellConsole.java
src/com/cyanogenmod/filemanager/util/CommandHelper.java

index 707b51b..14cce46 100644 (file)
@@ -83,6 +83,7 @@
 
   <!-- Process control and info -->
   <command commandId="pid_shell" commandPath="/system/xbin/echo" commandArgs="$$" />
+  <command commandId="pid_shell_cmds" commandPath="/system/bin/ps" commandArgs="| /system/xbin/grep -w %1$s | /system/xbin/awk '{print $2}' | /system/xbin/grep -v -w %1$s" />
   <command commandId="pid_cmd" commandPath="/system/bin/ps" commandArgs="| /system/xbin/grep %1$s | /system/xbin/grep -w %2$s | /system/xbin/awk '{print $2}'" />
   <command commandId="sendsignal" commandPath="/system/bin/kill" commandArgs="-%1$s %2$s" />
   <command commandId="terminate" commandPath="/system/bin/kill" commandArgs="%1$s" />
index 5d537a9..ea7b958 100644 (file)
@@ -350,6 +350,21 @@ public interface ExecutableCreator {
             NoSuchFileOrDirectory, InsufficientPermissionsException;
 
     /**
+     * Method that creates an executable for retrieve operating system process identifiers of a
+     * shell.
+     *
+     * @param pid The shell process id where the process is running
+     * @param processName The process name
+     * @return ProcessIdExecutable A {@link ProcessIdExecutable} executable implementation
+     * reference
+     * @throws CommandNotFoundException If the executable can't be created
+     * @throws NoSuchFileOrDirectory If the file or directory was not found
+     * @throws InsufficientPermissionsException If an operation requires elevated permissions
+     */
+    ProcessIdExecutable createProcessIdExecutable(int pid) throws CommandNotFoundException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException;
+
+    /**
      * Method that creates an executable for retrieve operating system process identifier of a
      * process.
      *
index cbcbce3..bdee764 100644 (file)
@@ -16,6 +16,8 @@
 
 package com.cyanogenmod.filemanager.commands;
 
+import java.util.List;
+
 /**
  * An interface that represents an executable for retrieve the process identifier
  * of a program.
@@ -26,5 +28,5 @@ public interface ProcessIdExecutable extends SyncResultExecutable {
      * {@inheritDoc}
      */
     @Override
-    Integer getResult();
+    List<Integer> getResult();
 }
index 1186515..1f680e4 100644 (file)
@@ -287,6 +287,15 @@ public class JavaExecutableCreator implements ExecutableCreator {
      * {@inheritDoc}
      */
     @Override
+    public ProcessIdExecutable createProcessIdExecutable(int pid)
+            throws CommandNotFoundException {
+        throw new CommandNotFoundException("Not implemented"); //$NON-NLS-1$
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public ProcessIdExecutable createProcessIdExecutable(int pid, String processName)
             throws CommandNotFoundException {
         throw new CommandNotFoundException("Not implemented"); //$NON-NLS-1$
index 922de7b..908b6b1 100644 (file)
@@ -326,6 +326,15 @@ public abstract class AsyncResultProgram
      * {@inheritDoc}
      */
     @Override
+    public final boolean isIndefinitelyWait() {
+        // Asynchronous programs should wait indefinitely for its nature
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public boolean isCancellable() {
         //By defect an asynchronous command is cancellable
         return true;
index abd021a..537e53a 100644 (file)
@@ -92,4 +92,12 @@ public class CopyCommand extends SyncResultProgram implements CopyExecutable {
     public MountPoint getDstWritableMountPoint() {
         return MountPointHelper.getMountPointFromDirectory(this.mDst);
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isIndefinitelyWait() {
+        return true;
+    }
 }
index 9dc80eb..51c4b7f 100644 (file)
@@ -91,4 +91,12 @@ public class DeleteDirCommand extends SyncResultProgram implements DeleteDirExec
     public MountPoint getDstWritableMountPoint() {
         return MountPointHelper.getMountPointFromDirectory(this.mFileName);
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isIndefinitelyWait() {
+        return true;
+    }
 }
index e4f202e..bf1b609 100644 (file)
@@ -91,4 +91,12 @@ public class DeleteFileCommand extends SyncResultProgram implements DeleteFileEx
     public MountPoint getDstWritableMountPoint() {
         return MountPointHelper.getMountPointFromDirectory(this.mFileName);
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isIndefinitelyWait() {
+        return true;
+    }
 }
index c6228d1..20758a0 100644 (file)
@@ -204,4 +204,12 @@ public class ListCommand extends SyncResultProgram implements ListExecutable {
             throw new ExecutionException("exitcode != 0 && != 1 && != 123"); //$NON-NLS-1$
         }
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isWaitOnNewDataReceipt() {
+        return true;
+    }
 }
index a91e508..2a208f2 100644 (file)
@@ -94,4 +94,12 @@ public class MoveCommand extends SyncResultProgram implements MoveExecutable {
     public MountPoint getDstWritableMountPoint() {
         return MountPointHelper.getMountPointFromDirectory(this.mDst);
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isIndefinitelyWait() {
+        return true;
+    }
 }
index fd99b92..ac1af1e 100644 (file)
@@ -25,6 +25,8 @@ import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.StringReader;
 import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
 
 
 /**
@@ -35,8 +37,9 @@ import java.text.ParseException;
 public class ProcessIdCommand extends SyncResultProgram implements ProcessIdExecutable {
 
     private static final String ID_SHELL = "pid_shell";  //$NON-NLS-1$
+    private static final String ID_SHELL_CMDS = "pid_shell_cmds";  //$NON-NLS-1$
     private static final String ID_CMD = "pid_cmd";  //$NON-NLS-1$
-    private Integer mPID;
+    private List<Integer> mPIDs;
 
     /**
      * Constructor of <code>ProcessIdCommand</code>.<br/>
@@ -50,6 +53,17 @@ public class ProcessIdCommand extends SyncResultProgram implements ProcessIdExec
 
     /**
      * Constructor of <code>ProcessIdCommand</code>.<br/>
+     * Use this to retrieve all PIDs running on a shell.
+     *
+     * @param pid The process identifier of the shell when the process is running
+     * @throws InvalidCommandDefinitionException If the command has an invalid definition
+     */
+    public ProcessIdCommand(int pid) throws InvalidCommandDefinitionException {
+        super(ID_SHELL_CMDS, new String[]{String.valueOf(pid)});
+    }
+
+    /**
+     * Constructor of <code>ProcessIdCommand</code>.<br/>
      * Use this to retrieve the PID of a command running on a shell.
      *
      * @param pid The process identifier of the shell when the process is running
@@ -66,7 +80,7 @@ public class ProcessIdCommand extends SyncResultProgram implements ProcessIdExec
     @Override
     public void parse(String in, String err) throws ParseException {
         //Release the return object
-        this.mPID = null;
+        this.mPIDs = new ArrayList<Integer>();
 
         // Check the in buffer to extract information
         BufferedReader br = null;
@@ -76,9 +90,13 @@ public class ProcessIdCommand extends SyncResultProgram implements ProcessIdExec
             if (szLine == null) {
                 throw new ParseException("no information", 0); //$NON-NLS-1$
             }
+            do {
+                // Add every PID
+                this.mPIDs.add(Integer.valueOf(szLine.trim()));
 
-            //Get the PID
-            this.mPID = Integer.valueOf(szLine.trim());
+                // Next line
+                szLine = br.readLine();
+            } while (szLine != null);
 
         } catch (IOException ioEx) {
             throw new ParseException(ioEx.getMessage(), 0);
@@ -104,8 +122,8 @@ public class ProcessIdCommand extends SyncResultProgram implements ProcessIdExec
      * {@inheritDoc}
      */
     @Override
-    public Integer getResult() {
-        return this.mPID;
+    public List<Integer> getResult() {
+        return this.mPIDs;
     }
 
     /**
index 1bcc5db..b6a736d 100644 (file)
@@ -21,6 +21,7 @@ import com.cyanogenmod.filemanager.console.CommandNotFoundException;
 import com.cyanogenmod.filemanager.console.ExecutionException;
 import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
 import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
+import com.cyanogenmod.filemanager.console.OperationTimeoutException;
 
 import java.io.OutputStream;
 
@@ -128,6 +129,30 @@ public abstract class Program extends Command implements Executable {
     }
 
     /**
+     * Returns whether the shell should wait indefinitely for the end of the command.
+     *
+     * @return boolean If shell should wait indefinitely for the end of the command
+     * @hide
+     */
+    @SuppressWarnings("static-method")
+    public boolean isIndefinitelyWait() {
+        return false;
+    }
+
+    /**
+     * Returns whether the shell shouldn't raise a {@link OperationTimeoutException} when
+     * the program didn't exited but new data was received.
+     *
+     * @return boolean If shell shouldn't raise a {@link OperationTimeoutException} if new
+     * data was received
+     * @hide
+     */
+    @SuppressWarnings("static-method")
+    public boolean isWaitOnNewDataReceipt() {
+        return false;
+    }
+
+    /**
      * Method that returns if the standard error must be
      * ignored safely by the shell, and don't check for errors
      * like <code>NoSuchFileOrDirectory</code> or
index b060381..19ddb20 100644 (file)
@@ -126,4 +126,12 @@ public class QuickFolderSearchCommand
         return true;
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isWaitOnNewDataReceipt() {
+        return true;
+    }
+
 }
index e474184..339ca09 100644 (file)
@@ -373,6 +373,19 @@ public class ShellExecutableCreator implements ExecutableCreator {
      * {@inheritDoc}
      */
     @Override
+    public ProcessIdExecutable createProcessIdExecutable(int pid)
+            throws CommandNotFoundException {
+        try {
+            return new ProcessIdCommand(pid);
+        } catch (InvalidCommandDefinitionException icdEx) {
+            throw new CommandNotFoundException("ProcessIdCommand", icdEx); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public ProcessIdExecutable createProcessIdExecutable(int pid, String processName)
             throws CommandNotFoundException {
         try {
index ec759fc..5cdc583 100644 (file)
@@ -68,10 +68,15 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
 
     private static final String TAG = "ShellConsole"; //$NON-NLS-1$
 
-    // A timeout of 5 seconds should be enough for no-debugging environments
+    // A timeout of 3 seconds should be enough for no-debugging environments
     private static final long DEFAULT_TIMEOUT =
             FileManagerApplication.isDebuggable() ? 20000L : 3000L;
 
+    // A maximum operation timeout independently of the isWaitOnNewDataReceipt
+    // of the program. A synchronous operation must not be more longer than
+    // MAX_OPERATION_TIMEOUT + DEFAULT_TIMEOUT
+    private static final long MAX_OPERATION_TIMEOUT = 30000L;
+
     private static final int DEFAULT_BUFFER = 512;
 
     //Shell References
@@ -89,6 +94,7 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
      */
     boolean mActive = false;
     private boolean mFinished = true;
+    private boolean mNewData = false;
     private Process mProc = null;
     /**
      * @hide
@@ -263,7 +269,12 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
             // wait to user response to SuperUser or SuperSu prompt (or whatever it is)
             // The rest of sync operations will run with a timeout.
             execute(processIdCmd, this.isPrivileged());
-            Integer pid = processIdCmd.getResult();
+            Integer pid = null;
+            try {
+                pid = processIdCmd.getResult().get(0);
+            } catch (Exception e) {
+                // Ignore
+            }
             if (pid == null) {
                 throw new ConsoleAllocException(
                         "can't retrieve the PID of the shell."); //$NON-NLS-1$
@@ -532,6 +543,7 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
                sb.append(FileHelper.NEWLINE);
                synchronized (this.mSync) {
                    this.mFinished = false;
+                   this.mNewData = false;
                    this.mOut.write(sb.toString().getBytes());
                }
             } catch (InvalidCommandDefinitionException icdEx) {
@@ -542,12 +554,25 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
             //Now, wait for buffers to be filled
             synchronized (this.mSync) {
                 if (!this.mFinished) {
-                    if (waitForSu || program instanceof AsyncResultProgram) {
+                    if (waitForSu || program.isIndefinitelyWait()) {
                         this.mSync.wait();
                     } else {
-                        this.mSync.wait(DEFAULT_TIMEOUT);
-                        if (!this.mFinished) {
-                            throw new OperationTimeoutException(DEFAULT_TIMEOUT, cmd);
+                        final long start = System.currentTimeMillis();
+                        while (true) {
+                            this.mSync.wait(DEFAULT_TIMEOUT);
+                            if (!this.mFinished) {
+                                final long end = System.currentTimeMillis();
+                                if (!program.isWaitOnNewDataReceipt() ||
+                                    !this.mNewData ||
+                                    (end - start >= MAX_OPERATION_TIMEOUT)) {
+                                    throw new OperationTimeoutException(end - start, cmd);
+                                }
+
+                                // Still waiting for program ending
+                                this.mNewData = false;
+                                continue;
+                            }
+                            break;
                         }
                     }
                 }
@@ -604,6 +629,12 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
             //Invocation finished. Now program.getResult() has the result of
             //the operation, if any exists
 
+        } catch (OperationTimeoutException otEx) {
+            try {
+                killCurrentCommand();
+            } catch (Exception e) { /**NON BLOCK **/}
+            throw otEx;
+
         } catch (IOException ioEx) {
             if (reallocate) {
                 realloc();
@@ -677,6 +708,9 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
                                 sb.append((char)r);
                             }
 
+                            // New data received
+                            onNewData();
+
                             //Check if the command has finished (and extract the control)
                             boolean finished = isCommandFinished(shell.mSbIn, sb);
 
@@ -757,6 +791,9 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
                                 sb.append(s);
                             }
 
+                            // New data received
+                            onNewData();
+
                             //Check if the command has finished (and extract the control)
                             boolean finished = isCommandFinished(shell.mSbIn, sb);
 
@@ -887,6 +924,9 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
                             toStdErr(sb.toString());
                         }
 
+                        // New data received
+                        onNewData();
+
                         //Has more data? Read with available as more as exists
                         //or maximum loop count is rebased
                         int count = 0;
@@ -923,6 +963,9 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
                                 break;
                             }
 
+                            // New data received
+                            onNewData();
+
                             //Wait for buffer to be filled
                             try {
                                 Thread.sleep(1L);
@@ -1080,6 +1123,16 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
     }
 
     /**
+     * New data was received
+     * @hide
+     */
+    void onNewData() {
+        synchronized (this.mSync) {
+            this.mNewData = true;
+        }
+    }
+
+    /**
      * Method that returns the exit code of the last executed command.
      *
      * @param stdin The standard in buffer
@@ -1129,10 +1182,6 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
      */
     private boolean killCurrentCommand() {
         synchronized (this.mSync) {
-            //Is synchronous program? Otherwise it can't be cancelled
-            if (!(this.mActiveCommand instanceof AsyncResultProgram)) {
-                return false;
-            }
             // Check background console
             try {
                 FileManagerApplication.getBackgroundConsole();
@@ -1141,31 +1190,38 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
                 return false;
             }
 
-            final AsyncResultProgram program = (AsyncResultProgram)this.mActiveCommand;
-            if (program.getCommand() != null) {
+            if (this.mActiveCommand.getCommand() != null) {
                 try {
-                    if (program.isCancellable()) {
+                    boolean isCancellable = true;
+                    if (this.mActiveCommand instanceof AsyncResultProgram) {
+                        final AsyncResultProgram asyncCmd =
+                                (AsyncResultProgram)this.mActiveCommand;
+                        isCancellable = asyncCmd.isCancellable();
+                    }
+
+                    if (isCancellable) {
                         try {
-                            //Get the PID in background
-                            Integer pid =
-                                    CommandHelper.getProcessId(
+                            //Get the PIDs in background
+                            List<Integer> pids =
+                                    CommandHelper.getProcessesIds(
                                             null,
                                             this.mShell.getPid(),
-                                            program.getCommand(),
                                             FileManagerApplication.getBackgroundConsole());
-                            if (pid != null) {
-                                CommandHelper.sendSignal(
-                                        null,
-                                        pid.intValue(),
-                                        FileManagerApplication.getBackgroundConsole());
-                                try {
-                                    //Wait for process kill
-                                    Thread.sleep(100L);
-                                } catch (Throwable ex) {
-                                    /**NON BLOCK**/
+                            for (Integer pid: pids) {
+                                if (pid != null) {
+                                    CommandHelper.sendSignal(
+                                            null,
+                                            pid.intValue(),
+                                            FileManagerApplication.getBackgroundConsole());
+                                    try {
+                                        //Wait for process to be killed
+                                        Thread.sleep(100L);
+                                    } catch (Throwable ex) {
+                                        /**NON BLOCK**/
+                                    }
                                 }
-                                return true;
                             }
+                            return true;
                         } finally {
                             // It's finished
                             this.mCancelled = true;
@@ -1176,7 +1232,7 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
                 } catch (Throwable ex) {
                     Log.w(TAG,
                             String.format("Unable to kill current program: %s", //$NON-NLS-1$
-                                    program.getCommand()), ex);
+                                    this.mActiveCommand.getCommand()), ex);
                 }
             }
         }
@@ -1192,10 +1248,6 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
      */
     private boolean sendSignalToCurrentCommand(SIGNAL signal) {
         synchronized (this.mSync) {
-            //Is synchronous program? Otherwise it can't be cancelled
-            if (!(this.mActiveCommand instanceof AsyncResultProgram)) {
-                return false;
-            }
             // Check background console
             try {
                 FileManagerApplication.getBackgroundConsole();
@@ -1204,32 +1256,39 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
                 return false;
             }
 
-            final AsyncResultProgram program = (AsyncResultProgram)this.mActiveCommand;
-            if (program.getCommand() != null) {
+            if (this.mActiveCommand.getCommand() != null) {
                 try {
-                    if (program.isCancellable()) {
+                    boolean isCancellable = true;
+                    if (this.mActiveCommand instanceof AsyncResultProgram) {
+                        final AsyncResultProgram asyncCmd =
+                                (AsyncResultProgram)this.mActiveCommand;
+                        isCancellable = asyncCmd.isCancellable();
+                    }
+
+                    if (isCancellable) {
                         try {
-                            //Get the PID in background
-                            Integer pid =
-                                    CommandHelper.getProcessId(
+                            //Get the PIDs in background
+                            List<Integer> pids =
+                                    CommandHelper.getProcessesIds(
                                             null,
                                             this.mShell.getPid(),
-                                            program.getCommand(),
                                             FileManagerApplication.getBackgroundConsole());
-                            if (pid != null) {
-                                CommandHelper.sendSignal(
-                                        null,
-                                        pid.intValue(),
-                                        signal,
-                                        FileManagerApplication.getBackgroundConsole());
-                                try {
-                                    //Wait for process kill
-                                    Thread.sleep(100L);
-                                } catch (Throwable ex) {
-                                    /**NON BLOCK**/
+                            for (Integer pid: pids) {
+                                if (pid != null) {
+                                    CommandHelper.sendSignal(
+                                            null,
+                                            pid.intValue(),
+                                            signal,
+                                            FileManagerApplication.getBackgroundConsole());
+                                    try {
+                                        //Wait for process to be signaled
+                                        Thread.sleep(100L);
+                                    } catch (Throwable ex) {
+                                        /**NON BLOCK**/
+                                    }
                                 }
-                                return true;
                             }
+                            return true;
                         } finally {
                             // It's finished
                             this.mCancelled = true;
@@ -1240,7 +1299,7 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
                 } catch (Throwable ex) {
                     Log.w(TAG,
                         String.format("Unable to send signal to current program: %s", //$NON-NLS-1$
-                                program.getCommand()), ex);
+                                this.mActiveCommand.getCommand()), ex);
                 }
             }
         }
index 31a0753..85b6a03 100644 (file)
@@ -1009,6 +1009,39 @@ public final class CommandHelper {
     }
 
     /**
+     * Method that retrieves the process identifier of all the processes (a program
+     * owned by the main process of this application).
+     *
+     * @param context The current context (needed if console == null)
+     * @param pid The process id of the shell where the command is running
+     * @param console The console in which execute the program. <code>null</code>
+     * to attach to the default console
+     * @return List<Integer> The processes identifiers of the program or <code>null</code> if not exists
+     * @throws FileNotFoundException If the initial directory not exists
+     * @throws IOException If initial directory couldn't be checked
+     * @throws InvalidCommandDefinitionException If the command has an invalid definition
+     * @throws NoSuchFileOrDirectory If the file or directory was not found
+     * @throws ConsoleAllocException If the console can't be allocated
+     * @throws InsufficientPermissionsException If an operation requires elevated permissions
+     * @throws CommandNotFoundException If the command was not found
+     * @throws OperationTimeoutException If the operation exceeded the maximum time of wait
+     * @throws ExecutionException If the operation returns a invalid exit code
+     * @see ProcessIdExecutable
+     */
+    public static List<Integer> getProcessesIds(
+            Context context, int pid, Console console)
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
+            ExecutionException, InvalidCommandDefinitionException {
+        Console c = ensureConsole(context, console);
+        ProcessIdExecutable executable =
+                c.getExecutableFactory().newCreator().createProcessIdExecutable(pid);
+        execute(context, executable, c);
+        return executable.getResult();
+    }
+
+    /**
      * Method that retrieves the process identifier of a process (a program
      * owned by the main process of this application).
      *
@@ -1039,7 +1072,11 @@ public final class CommandHelper {
         ProcessIdExecutable executable =
                 c.getExecutableFactory().newCreator().createProcessIdExecutable(pid, processName);
         execute(context, executable, c);
-        return executable.getResult();
+        List<Integer> pids = executable.getResult();
+        if (pids != null && pids.size() > 0) {
+            return pids.get(0);
+        }
+        return null;
     }
 
     /**