FileDocCategorySizeDatePackage
EmulatorConsole.javaAPI DocAndroid 1.5 API25908Wed May 06 22:41:08 BST 2009com.android.ddmlib

EmulatorConsole

public final class EmulatorConsole extends Object
Provides control over emulated hardware of the Android emulator.

This is basically a wrapper around the command line console normally used with telnet.

Regarding line termination handling:
One of the issues is that the telnet protocol requires usage of \r\n. Most implementations don't enforce it (the dos one does). In this particular case, this is mostly irrelevant since we don't use telnet in Java, but that means we want to make sure we use the same line termination than what the console expects. The console code removes \r and waits for \n.

However this means you may receive \r\n when reading from the console.

This API will change in the near future.

Fields Summary
private static final String
DEFAULT_ENCODING
private static final int
WAIT_TIME
private static final int
STD_TIMEOUT
private static final String
HOST
private static final String
COMMAND_PING
private static final String
COMMAND_AVD_NAME
private static final String
COMMAND_KILL
private static final String
COMMAND_GSM_STATUS
private static final String
COMMAND_GSM_CALL
private static final String
COMMAND_GSM_CANCEL_CALL
private static final String
COMMAND_GSM_DATA
private static final String
COMMAND_GSM_VOICE
private static final String
COMMAND_SMS_SEND
private static final String
COMMAND_NETWORK_STATUS
private static final String
COMMAND_NETWORK_SPEED
private static final String
COMMAND_NETWORK_LATENCY
private static final String
COMMAND_GPS
private static final Pattern
RE_KO
public static final int[]
MIN_LATENCIES
Array of delay values: no delay, gprs, edge/egprs, umts/3d
public final int[]
DOWNLOAD_SPEEDS
Array of download speeds: full speed, gsm, hscsd, gprs, edge/egprs, umts/3g, hsdpa.
public static final String[]
NETWORK_SPEEDS
Arrays of valid network speeds
public static final String[]
NETWORK_LATENCIES
Arrays of valid network latencies
public static final String
RESULT_OK
private static final Pattern
sEmulatorRegexp
private static final Pattern
sVoiceStatusRegexp
private static final Pattern
sDataStatusRegexp
private static final Pattern
sDownloadSpeedRegexp
private static final Pattern
sMinLatencyRegexp
private static final HashMap
sEmulators
private int
mPort
private SocketChannel
mSocketChannel
private byte[]
mBuffer
Constructors Summary
private EmulatorConsole(int port)

        super();
        mPort = port;
    
Methods Summary
private static synchronized voidRemoveConsole(int port)
Removes the console object associated with a port from the map.

param
port The port of the console to remove.

        sEmulators.remove(port);
    
public synchronized java.lang.Stringcall(java.lang.String number)
Initiate an incoming call on the emulator.

param
number a string representing the calling number.
return
{@link #RESULT_OK} if success, an error String otherwise.

        String command = String.format(COMMAND_GSM_CALL, number);
        return processCommand(command);
    
public synchronized java.lang.StringcancelCall(java.lang.String number)
Cancels a current call.

param
number the number of the call to cancel
return
{@link #RESULT_OK} if success, an error String otherwise.

        String command = String.format(COMMAND_GSM_CANCEL_CALL, number);
        return processCommand(command);
    
private booleanendsWithOK(int currentPosition)
Returns true if the 4 characters *before* the current position are "OK\r\n"

param
currentPosition The current position

        if (mBuffer[currentPosition-1] == '\n" &&
                mBuffer[currentPosition-2] == '\r" &&
                mBuffer[currentPosition-3] == 'K" &&
                mBuffer[currentPosition-4] == 'O") {
            return true;
        }

        return false;
    
public synchronized java.lang.StringgetAvdName()

        if (sendCommand(COMMAND_AVD_NAME)) {
            String[] result = readLines();
            if (result != null && result.length == 2) { // this should be the name on first line,
                                                        // and ok on 2nd line
                return result[0];
            } else {
                // try to see if there's a message after KO
                Matcher m = RE_KO.matcher(result[result.length-1]);
                if (m.matches()) {
                    return m.group(1);
                }
            }
        }
        
        return null;
    
public static synchronized com.android.ddmlib.EmulatorConsolegetConsole(Device d)
Returns an {@link EmulatorConsole} object for the given {@link Device}. This can be an already existing console, or a new one if it hadn't been created yet.

param
d The device that the console links to.
return
an EmulatorConsole object or null if the connection failed.


                                                       
          
        // we need to make sure that the device is an emulator
        Matcher m = sEmulatorRegexp.matcher(d.serialNumber);
        if (m.matches()) {
            // get the port number. This is the console port.
            int port;
            try {
                port = Integer.parseInt(m.group(1));
                if (port <= 0) {
                    return null;
                }
            } catch (NumberFormatException e) {
                // looks like we failed to get the port number. This is a bit strange since
                // it's coming from a regexp that only accept digit, but we handle the case
                // and return null.
                return null;
            }

            EmulatorConsole console = sEmulators.get(port);

            if (console != null) {
                // if the console exist, we ping the emulator to check the connection.
                if (console.ping() == false) {
                    RemoveConsole(console.mPort);
                    console = null;
                }
            }

            if (console == null) {
                // no console object exists for this port so we create one, and start
                // the connection.
                console = new EmulatorConsole(port);
                if (console.start()) {
                    sEmulators.put(port, console);
                } else {
                    console = null;
                }
            }

            return console;
        }

        return null;
    
public synchronized com.android.ddmlib.EmulatorConsole$GsmStatusgetGsmStatus()
Returns the current gsm status of the emulator

return
a {@link GsmStatus} object containing the gms status, or null if the query failed.

        if (sendCommand(COMMAND_GSM_STATUS)) {
            /*
             * result is in the format:
             * gsm status
             * gsm voice state: home
             * gsm data state:  home
             */

            String[] result = readLines();
            if (isValid(result)) {

                GsmStatus status = new GsmStatus();

                // let's not rely on the order of the output, and simply loop through
                // the line testing the regexp.
                for (String line : result) {
                    Matcher m = sVoiceStatusRegexp.matcher(line);
                    if (m.matches()) {
                        // get the string value
                        String value = m.group(1);

                        // get the index from the list
                        status.voice = GsmMode.getEnum(value.toLowerCase());

                        // move on to next line.
                        continue;
                    }

                    m = sDataStatusRegexp.matcher(line);
                    if (m.matches()) {
                        // get the string value
                        String value = m.group(1);

                        // get the index from the list
                        status.data = GsmMode.getEnum(value.toLowerCase());

                        // move on to next line.
                        continue;
                    }
                }

                return status;
            }
        }

        return null;
    
private intgetLatencyIndex(java.lang.String value)

        try {
            // get the int value
            int latency = Integer.parseInt(value);

            // check for the speed from the index
            for (int i = 0 ; i < MIN_LATENCIES.length; i++) {
                if (MIN_LATENCIES[i] == latency) {
                    return i;
                }
            }
        } catch (NumberFormatException e) {
            // Do nothing, we'll just return -1.
        }

        return -1;
    
public synchronized com.android.ddmlib.EmulatorConsole$NetworkStatusgetNetworkStatus()
Get the network status of the emulator.

return
a {@link NetworkStatus} object containing the {@link GsmStatus}, or null if the query failed.

        if (sendCommand(COMMAND_NETWORK_STATUS)) {
            /* Result is in the format
                Current network status:
                download speed:      14400 bits/s (1.8 KB/s)
                upload speed:        14400 bits/s (1.8 KB/s)
                minimum latency:  0 ms
                maximum latency:  0 ms
             */
            String[] result = readLines();

            if (isValid(result)) {
                // we only compare agains the min latency and the download speed
                // let's not rely on the order of the output, and simply loop through
                // the line testing the regexp.
                NetworkStatus status = new NetworkStatus();
                for (String line : result) {
                    Matcher m = sDownloadSpeedRegexp.matcher(line);
                    if (m.matches()) {
                        // get the string value
                        String value = m.group(1);

                        // get the index from the list
                        status.speed = getSpeedIndex(value);

                        // move on to next line.
                        continue;
                    }

                    m = sMinLatencyRegexp.matcher(line);
                    if (m.matches()) {
                        // get the string value
                        String value = m.group(1);

                        // get the index from the list
                        status.latency = getLatencyIndex(value);

                        // move on to next line.
                        continue;
                    }
                }

                return status;
            }
        }

        return null;
    
private intgetSpeedIndex(java.lang.String value)

        try {
            // get the int value
            int speed = Integer.parseInt(value);

            // check for the speed from the index
            for (int i = 0 ; i < DOWNLOAD_SPEEDS.length; i++) {
                if (DOWNLOAD_SPEEDS[i] == speed) {
                    return i;
                }
            }
        } catch (NumberFormatException e) {
            // Do nothing, we'll just return -1.
        }

        return -1;
    
private booleanisValid(java.lang.String[] result)
Returns true if the last line of the result does not start with KO

        if (result != null && result.length > 0) {
            return !(RE_KO.matcher(result[result.length-1]).matches());
        }
        return false;
    
public synchronized voidkill()
Sends a KILL command to the emulator.

        if (sendCommand(COMMAND_KILL)) {
            RemoveConsole(mPort);
        }
    
private booleanlastLineIsKO(int currentPosition)
Returns true if the last line starts with KO and is also terminated by \r\n

param
currentPosition the current position

        // first check that the last 2 characters are CRLF
        if (mBuffer[currentPosition-1] != '\n" ||
                mBuffer[currentPosition-2] != '\r") {
            return false;
        }

        // now loop backward looking for the previous CRLF, or the beginning of the buffer
        int i = 0;
        for (i = currentPosition-3 ; i >= 0; i--) {
            if (mBuffer[i] == '\n") {
                // found \n!
                if (i > 0 && mBuffer[i-1] == '\r") {
                    // found \r!
                    break;
                }
            }
        }

        // here it is either -1 if we reached the start of the buffer without finding
        // a CRLF, or the position of \n. So in both case we look at the characters at i+1 and i+2
        if (mBuffer[i+1] == 'K" && mBuffer[i+2] == 'O") {
            // found error!
            return true;
        }

        return false;
    
private synchronized booleanping()
Ping the emulator to check if the connection is still alive.

return
true if the connection is alive.

        // it looks like we can send stuff, even when the emulator quit, but we can't read
        // from the socket. So we check the return of readLines()
        if (sendCommand(COMMAND_PING)) {
            return readLines() != null;
        }

        return false;
    
private java.lang.StringprocessCommand(java.lang.String command)
Sends a command to the emulator and parses its answer.

param
command the command to send.
return
{@link #RESULT_OK} if success, an error message otherwise.

        if (sendCommand(command)) {
            String[] result = readLines();

            if (result != null && result.length > 0) {
                Matcher m = RE_KO.matcher(result[result.length-1]);
                if (m.matches()) {
                    return m.group(1);
                }
                return RESULT_OK;
            }

            return "Unable to communicate with the emulator";
        }

        return "Unable to send command to the emulator";
    
private java.lang.String[]readLines()
Reads line from the console socket. This call is blocking until we read the lines:
  • OK\r\n
  • KO\r\n

return
the array of strings read from the emulator.

        try {
            ByteBuffer buf = ByteBuffer.wrap(mBuffer, 0, mBuffer.length);
            int numWaits = 0;
            boolean stop = false;
            
            while (buf.position() != buf.limit() && stop == false) {
                int count;

                count = mSocketChannel.read(buf);
                if (count < 0) {
                    return null;
                } else if (count == 0) {
                    if (numWaits * WAIT_TIME > STD_TIMEOUT) {
                        return null;
                    }
                    // non-blocking spin
                    try {
                        Thread.sleep(WAIT_TIME);
                    } catch (InterruptedException ie) {
                    }
                    numWaits++;
                } else {
                    numWaits = 0;
                }

                // check the last few char aren't OK. For a valid message to test
                // we need at least 4 bytes (OK/KO + \r\n)
                if (buf.position() >= 4) {
                    int pos = buf.position();
                    if (endsWithOK(pos) || lastLineIsKO(pos)) {
                        stop = true;
                    }
                }
            }

            String msg = new String(mBuffer, 0, buf.position(), DEFAULT_ENCODING);
            return msg.split("\r\n"); //$NON-NLS-1$
        } catch (IOException e) {
            return null;
        }
    
private booleansendCommand(java.lang.String command)
Sends a command to the emulator console.

param
command The command string. MUST BE TERMINATED BY \n.
return
true if success

        boolean result = false;
        try {
            byte[] bCommand;
            try {
                bCommand = command.getBytes(DEFAULT_ENCODING);
            } catch (UnsupportedEncodingException e) {
                // wrong encoding...
                return result;
            }

            // write the command
            AdbHelper.write(mSocketChannel, bCommand, bCommand.length, AdbHelper.STD_TIMEOUT);

            result = true;
        } catch (IOException e) {
            return false;
        } finally {
            if (result == false) {
                // FIXME connection failed somehow, we need to disconnect the console.
                RemoveConsole(mPort);
            }
        }

        return result;
    
public synchronized java.lang.StringsendLocation(double longitude, double latitude, double elevation)

        
        Calendar c = Calendar.getInstance();
        
        double absLong = Math.abs(longitude);
        int longDegree = (int)Math.floor(absLong);
        char longDirection = 'E";
        if (longitude < 0) {
            longDirection = 'W";
        }
        
        double longMinute = (absLong - Math.floor(absLong)) * 60;

        double absLat = Math.abs(latitude);
        int latDegree = (int)Math.floor(absLat);
        char latDirection = 'N";
        if (latitude < 0) {
            latDirection = 'S";
        }
        
        double latMinute = (absLat - Math.floor(absLat)) * 60;
        
        String command = String.format(COMMAND_GPS,
                c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE),
                c.get(Calendar.SECOND), c.get(Calendar.MILLISECOND),
                latDegree, latMinute, latDirection,
                longDegree, longMinute, longDirection);
        
        return processCommand(command);
    
public synchronized java.lang.StringsendSms(java.lang.String number, java.lang.String message)
Sends an SMS to the emulator

param
number The sender phone number
param
message The SMS message. \ characters must be escaped. The carriage return is the 2 character sequence {'\', 'n' }
return
{@link #RESULT_OK} if success, an error String otherwise.

        String command = String.format(COMMAND_SMS_SEND, number, message);
        return processCommand(command);
    
public synchronized java.lang.StringsetGsmDataMode(com.android.ddmlib.EmulatorConsole$GsmMode mode)
Sets the GSM data mode.

param
mode the {@link GsmMode} value
return
{@link #RESULT_OK} if success, an error String otherwise.
throws
InvalidParameterException if mode is an invalid value.

        if (mode == GsmMode.UNKNOWN) {
            throw new InvalidParameterException();
        }

        String command = String.format(COMMAND_GSM_DATA, mode.getTag());
        return processCommand(command);
    
public synchronized java.lang.StringsetGsmVoiceMode(com.android.ddmlib.EmulatorConsole$GsmMode mode)
Sets the GSM voice mode.

param
mode the {@link GsmMode} value.
return
RESULT_OK if success, an error String otherwise.
throws
InvalidParameterException if mode is an invalid value.

        if (mode == GsmMode.UNKNOWN) {
            throw new InvalidParameterException();
        }

        String command = String.format(COMMAND_GSM_VOICE, mode.getTag());
        return processCommand(command);
    
public synchronized java.lang.StringsetNetworkLatency(int selectionIndex)
Sets the network latency.

param
selectionIndex The index in the {@link #NETWORK_LATENCIES} table.
return
{@link #RESULT_OK} if success, an error String otherwise.

        String command = String.format(COMMAND_NETWORK_LATENCY, NETWORK_LATENCIES[selectionIndex]);
        return processCommand(command);
    
public synchronized java.lang.StringsetNetworkSpeed(int selectionIndex)
Sets the network speed.

param
selectionIndex The index in the {@link #NETWORK_SPEEDS} table.
return
{@link #RESULT_OK} if success, an error String otherwise.

        String command = String.format(COMMAND_NETWORK_SPEED, NETWORK_SPEEDS[selectionIndex]);
        return processCommand(command);
    
private booleanstart()
Starts the connection of the console.

return
true if success.


        InetSocketAddress socketAddr;
        try {
            InetAddress hostAddr = InetAddress.getByName(HOST);
            socketAddr = new InetSocketAddress(hostAddr, mPort);
        } catch (UnknownHostException e) {
            return false;
        }

        try {
            mSocketChannel = SocketChannel.open(socketAddr);
        } catch (IOException e1) {
            return false;
        }

        // read some stuff from it
        readLines();

        return true;