src/main/java/com/simpligility/maven/plugins/android/AbstractEmulatorMojo.java
boolean waitUntilDeviceIsBootedOrTimeout( AndroidDebugBridge androidDebugBridge )
            throws MojoExecutionException
    {
        final long timeout = System.currentTimeMillis() + Long.parseLong( parsedWait );
        IDevice myEmulator;
        boolean devOnline;
        boolean sysBootCompleted = false;
        long remainingTime = 0;

        //If necessary, wait until the device is online or the specified timeout is reached
        boolean waitingForConnection = false;
        do
        {
            myEmulator = findExistingEmulator( Arrays.asList( androidDebugBridge.getDevices() ) );
            devOnline = ( myEmulator != null ) && ( myEmulator.isOnline() );
            if ( devOnline )
            {
                break;
            }
            else
            {
                myEmulator = null;
            }

            if ( !waitingForConnection )
            {
                waitingForConnection = true;
                getLog().info( "Waiting for the device to go online..." );
            }
            try
            {
                Thread.sleep( MILLIS_TO_SLEEP_BETWEEN_DEVICE_ONLINE_CHECKS );
            }
            catch ( InterruptedException e )
            {
                throw new MojoExecutionException( "Interrupted waiting for device to become ready" );
            }

            remainingTime = timeout - System.currentTimeMillis();
        } while ( remainingTime > 0 );

        if ( devOnline )
        {
            boolean waitingForBootCompleted = false;
            final String[] bootIndicatorPropValues = new String[ BOOT_INDICATOR_PROP_NAMES.length ];
            boolean anyTargetStateReached = false;
            boolean requiredTargetStatesReached = false;

            // If necessary, wait until the device's system is booted or the specified timeout is reached
            do
            {
                try
                {
                    // update state flags...
                    anyTargetStateReached = false;
                    requiredTargetStatesReached = true;

                    for ( int indicatorProp = 0; indicatorProp < BOOT_INDICATOR_PROP_NAMES.length; ++indicatorProp )
                    {
                        // issue an un-cached property request
                        boolean targetStateReached =
                                (
                                        bootIndicatorPropValues[indicatorProp] != null
                                                &&  bootIndicatorPropValues[indicatorProp]
                                                .equals( BOOT_INDICATOR_PROP_TARGET_VALUES[indicatorProp] )
                                );
                        if ( !targetStateReached )
                        {
                            // (re)query
                            bootIndicatorPropValues[indicatorProp] =
                                    myEmulator.getPropertySync( BOOT_INDICATOR_PROP_NAMES[indicatorProp] );
                            targetStateReached =
                                    (
                                            bootIndicatorPropValues[indicatorProp] != null
                                                    &&  bootIndicatorPropValues[indicatorProp]
                                                    .equals( BOOT_INDICATOR_PROP_TARGET_VALUES[indicatorProp] )
                                    );
                        }
                        anyTargetStateReached |= targetStateReached;
                        requiredTargetStatesReached &=
                                BOOT_INDICATOR_PROP_WAIT_FOR[indicatorProp] ? targetStateReached : true;

                        getLog().debug( BOOT_INDICATOR_PROP_NAMES[indicatorProp]
                                        + " : " +  bootIndicatorPropValues[indicatorProp]
                                        + ( targetStateReached ? " == " : " != " )
                                        + BOOT_INDICATOR_PROP_TARGET_VALUES[indicatorProp]
                                        + " [" + ( targetStateReached ? "OK" : "PENDING" ) + ']'
                        );
                    }
                }
                catch ( TimeoutException e )
                {
                    // TODO Abort here? Not too problematic since timeouts are used
                    // optimistically ignore this exception and continue...
                }
                catch ( AdbCommandRejectedException e )
                {
                    // TODO Abort here? Not too problematic since timeouts are used
                    // optimistically ignore this exception and continue...
                }
                catch ( ShellCommandUnresponsiveException e )
                {
                    // TODO Abort here? Not too problematic since timeouts are used
                    // optimistically ignore this exception and continue...
                }
                catch ( IOException e )
                {
                    throw new MojoExecutionException( "IO error during status request" , e );
                }

                remainingTime = timeout - System.currentTimeMillis();

                if ( remainingTime > 0 )
                {
                    // consider the boot process to be finished, if all required states have been reached
                    sysBootCompleted = requiredTargetStatesReached;
                }
                else
                {
                    // on timeout, use any indicator
                    sysBootCompleted = anyTargetStateReached;
                }

                if ( remainingTime > 0 && !sysBootCompleted )
                {
                    if ( !waitingForBootCompleted )
                    {
                        waitingForBootCompleted = true;
                        getLog().info( "Waiting for the device to finish booting..." );
                    }

                    try
                    {
                        Thread.sleep( MILLIS_TO_SLEEP_BETWEEN_SYS_BOOTED_CHECKS );
                    }
                    catch ( InterruptedException e )
                    {
                        throw new MojoExecutionException(
                                "Interrupted while waiting for the device to finish booting" );
                    }
                }
            } while ( !sysBootCompleted && remainingTime > 0 );
            if ( sysBootCompleted && remainingTime < START_TIMEOUT_REMAINING_TIME_WARNING_THRESHOLD )
            {
                getLog().warn(
                        "Boot indicators have been signalled, but remaining time was " + remainingTime + " ms" );
            }
        }
        return sysBootCompleted;
    }
boolean waitUntilDeviceIsBootedOrTimeout( AndroidDebugBridge androidDebugBridge )
            throws MojoExecutionException
    {
        final long timeout = System.currentTimeMillis() + Long.parseLong( parsedWait );
        IDevice myEmulator;
        boolean devOnline;
        boolean sysBootCompleted = false;
        long remainingTime = 0;

        //If necessary, wait until the device is online or the specified timeout is reached
        boolean waitingForConnection = false;
        do
        {
            myEmulator = findExistingEmulator( Arrays.asList( androidDebugBridge.getDevices() ) );
            devOnline = ( myEmulator != null ) && ( myEmulator.isOnline() );
            if ( devOnline )
            {
                break;
            }
            else
            {
                myEmulator = null;
            }

            if ( !waitingForConnection )
            {
                waitingForConnection = true;
                getLog().info( "Waiting for the device to go online..." );
            }
            try
            {
                Thread.sleep( MILLIS_TO_SLEEP_BETWEEN_DEVICE_ONLINE_CHECKS );
            }
            catch ( InterruptedException e )
            {
                throw new MojoExecutionException( "Interrupted waiting for device to become ready" );
            }

            remainingTime = timeout - System.currentTimeMillis();
        } while ( remainingTime > 0 );

        if ( devOnline )
        {
            boolean waitingForBootCompleted = false;
            final String[] bootIndicatorPropValues = new String[ BOOT_INDICATOR_PROP_NAMES.length ];
            boolean anyTargetStateReached = false;
            boolean requiredTargetStatesReached = false;

            // If necessary, wait until the device's system is booted or the specified timeout is reached
            do
            {
                try
                {
                    // update state flags...
                    anyTargetStateReached = false;
                    requiredTargetStatesReached = true;

                    for ( int indicatorProp = 0; indicatorProp < BOOT_INDICATOR_PROP_NAMES.length; ++indicatorProp )
                    {
                        // issue an un-cached property request
                        boolean targetStateReached =
                                (
                                        bootIndicatorPropValues[indicatorProp] != null
                                                &&  bootIndicatorPropValues[indicatorProp]
                                                .equals( BOOT_INDICATOR_PROP_TARGET_VALUES[indicatorProp] )
                                );
                        if ( !targetStateReached )
                        {
                            // (re)query
                            bootIndicatorPropValues[indicatorProp] =
                                    myEmulator.getPropertySync( BOOT_INDICATOR_PROP_NAMES[indicatorProp] );
                            targetStateReached =
                                    (
                                            bootIndicatorPropValues[indicatorProp] != null
                                                    &&  bootIndicatorPropValues[indicatorProp]
                                                    .equals( BOOT_INDICATOR_PROP_TARGET_VALUES[indicatorProp] )
                                    );
                        }
                        anyTargetStateReached |= targetStateReached;
                        requiredTargetStatesReached &=
                                BOOT_INDICATOR_PROP_WAIT_FOR[indicatorProp] ? targetStateReached : true;

                        getLog().debug( BOOT_INDICATOR_PROP_NAMES[indicatorProp]
                                        + " : " +  bootIndicatorPropValues[indicatorProp]
                                        + ( targetStateReached ? " == " : " != " )
                                        + BOOT_INDICATOR_PROP_TARGET_VALUES[indicatorProp]
                                        + " [" + ( targetStateReached ? "OK" : "PENDING" ) + ']'
                        );
                    }
                }
                catch ( TimeoutException e )
                {
                    // TODO Abort here? Not too problematic since timeouts are used
                    // optimistically ignore this exception and continue...
                }
                catch ( AdbCommandRejectedException e )
                {
                    // TODO Abort here? Not too problematic since timeouts are used
                    // optimistically ignore this exception and continue...
                }
                catch ( ShellCommandUnresponsiveException e )
                {
                    // TODO Abort here? Not too problematic since timeouts are used
                    // optimistically ignore this exception and continue...
                }
                catch ( IOException e )
                {
                    throw new MojoExecutionException( "IO error during status request" , e );
                }

                remainingTime = timeout - System.currentTimeMillis();

                if ( remainingTime > 0 )
                {
                    // consider the boot process to be finished, if all required states have been reached
                    sysBootCompleted = requiredTargetStatesReached;
                }
                else
                {
                    // on timeout, use any indicator
                    sysBootCompleted = anyTargetStateReached;
                }

                if ( remainingTime > 0 && !sysBootCompleted )
                {
                    if ( !waitingForBootCompleted )
                    {
                        waitingForBootCompleted = true;
                        getLog().info( "Waiting for the device to finish booting..." );
                    }

                    try
                    {
                        Thread.sleep( MILLIS_TO_SLEEP_BETWEEN_SYS_BOOTED_CHECKS );
                    }
                    catch ( InterruptedException e )
                    {
                        throw new MojoExecutionException(
                                "Interrupted while waiting for the device to finish booting" );
                    }
                }
            } while ( !sysBootCompleted && remainingTime > 0 );
            if ( sysBootCompleted && remainingTime < START_TIMEOUT_REMAINING_TIME_WARNING_THRESHOLD )
            {
                getLog().warn(
                        "Boot indicators have been signalled, but remaining time was " + remainingTime + " ms" );
            }
        }
        return sysBootCompleted;
    }
TODO Higher default timeout(s)?: Perhaps at least for emulators, since they are probably booted or even created on demand