1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.ddmlib; 18 19 import java.io.IOException; 20 import java.io.UnsupportedEncodingException; 21 import java.net.InetAddress; 22 import java.net.InetSocketAddress; 23 import java.net.UnknownHostException; 24 import java.nio.ByteBuffer; 25 import java.nio.channels.SocketChannel; 26 import java.security.InvalidParameterException; 27 import java.util.Calendar; 28 import java.util.HashMap; 29 import java.util.regex.Matcher; 30 import java.util.regex.Pattern; 31 32 /** 33 * Provides control over emulated hardware of the Android emulator. 34 * <p/>This is basically a wrapper around the command line console normally used with telnet. 35 *<p/> 36 * Regarding line termination handling:<br> 37 * One of the issues is that the telnet protocol <b>requires</b> usage of <code>\r\n</code>. Most 38 * implementations don't enforce it (the dos one does). In this particular case, this is mostly 39 * irrelevant since we don't use telnet in Java, but that means we want to make 40 * sure we use the same line termination than what the console expects. The console 41 * code removes <code>\r</code> and waits for <code>\n</code>. 42 * <p/>However this means you <i>may</i> receive <code>\r\n</code> when reading from the console. 43 * <p/> 44 * <b>This API will change in the near future.</b> 45 */ 46 public final class EmulatorConsole { 47 48 private final static String DEFAULT_ENCODING = "ISO-8859-1"; //$NON-NLS-1$ 49 50 private final static int WAIT_TIME = 5; // spin-wait sleep, in ms 51 52 private final static int STD_TIMEOUT = 5000; // standard delay, in ms 53 54 private final static String HOST = "127.0.0.1"; //$NON-NLS-1$ 55 56 private final static String COMMAND_PING = "help\r\n"; //$NON-NLS-1$ 57 private final static String COMMAND_AVD_NAME = "avd name\r\n"; //$NON-NLS-1$ 58 private final static String COMMAND_KILL = "kill\r\n"; //$NON-NLS-1$ 59 private final static String COMMAND_GSM_STATUS = "gsm status\r\n"; //$NON-NLS-1$ 60 private final static String COMMAND_GSM_CALL = "gsm call %1$s\r\n"; //$NON-NLS-1$ 61 private final static String COMMAND_GSM_CANCEL_CALL = "gsm cancel %1$s\r\n"; //$NON-NLS-1$ 62 private final static String COMMAND_GSM_DATA = "gsm data %1$s\r\n"; //$NON-NLS-1$ 63 private final static String COMMAND_GSM_VOICE = "gsm voice %1$s\r\n"; //$NON-NLS-1$ 64 private final static String COMMAND_SMS_SEND = "sms send %1$s %2$s\r\n"; //$NON-NLS-1$ 65 private final static String COMMAND_NETWORK_STATUS = "network status\r\n"; //$NON-NLS-1$ 66 private final static String COMMAND_NETWORK_SPEED = "network speed %1$s\r\n"; //$NON-NLS-1$ 67 private final static String COMMAND_NETWORK_LATENCY = "network delay %1$s\r\n"; //$NON-NLS-1$ 68 private final static String COMMAND_GPS = 69 "geo nmea $GPGGA,%1$02d%2$02d%3$02d.%4$03d," + //$NON-NLS-1$ 70 "%5$03d%6$09.6f,%7$c,%8$03d%9$09.6f,%10$c," + //$NON-NLS-1$ 71 "1,10,0.0,0.0,0,0.0,0,0.0,0000\r\n"; //$NON-NLS-1$ 72 73 private final static Pattern RE_KO = Pattern.compile("KO:\\s+(.*)"); //$NON-NLS-1$ 74 75 /** 76 * Array of delay values: no delay, gprs, edge/egprs, umts/3d 77 */ 78 public final static int[] MIN_LATENCIES = new int[] { 79 0, // No delay 80 150, // gprs 81 80, // edge/egprs 82 35 // umts/3g 83 }; 84 85 /** 86 * Array of download speeds: full speed, gsm, hscsd, gprs, edge/egprs, umts/3g, hsdpa. 87 */ 88 public final int[] DOWNLOAD_SPEEDS = new int[] { 89 0, // full speed 90 14400, // gsm 91 43200, // hscsd 92 80000, // gprs 93 236800, // edge/egprs 94 1920000, // umts/3g 95 14400000 // hsdpa 96 }; 97 98 /** Arrays of valid network speeds */ 99 public final static String[] NETWORK_SPEEDS = new String[] { 100 "full", //$NON-NLS-1$ 101 "gsm", //$NON-NLS-1$ 102 "hscsd", //$NON-NLS-1$ 103 "gprs", //$NON-NLS-1$ 104 "edge", //$NON-NLS-1$ 105 "umts", //$NON-NLS-1$ 106 "hsdpa", //$NON-NLS-1$ 107 }; 108 109 /** Arrays of valid network latencies */ 110 public final static String[] NETWORK_LATENCIES = new String[] { 111 "none", //$NON-NLS-1$ 112 "gprs", //$NON-NLS-1$ 113 "edge", //$NON-NLS-1$ 114 "umts", //$NON-NLS-1$ 115 }; 116 117 /** Gsm Mode enum. */ 118 public static enum GsmMode { 119 UNKNOWN((String)null), 120 UNREGISTERED(new String[] { "unregistered", "off" }), 121 HOME(new String[] { "home", "on" }), 122 ROAMING("roaming"), 123 SEARCHING("searching"), 124 DENIED("denied"); 125 126 private final String[] tags; 127 GsmMode(String tag)128 GsmMode(String tag) { 129 if (tag != null) { 130 this.tags = new String[] { tag }; 131 } else { 132 this.tags = new String[0]; 133 } 134 } 135 GsmMode(String[] tags)136 GsmMode(String[] tags) { 137 this.tags = tags; 138 } 139 getEnum(String tag)140 public static GsmMode getEnum(String tag) { 141 for (GsmMode mode : values()) { 142 for (String t : mode.tags) { 143 if (t.equals(tag)) { 144 return mode; 145 } 146 } 147 } 148 return UNKNOWN; 149 } 150 151 /** 152 * Returns the first tag of the enum. 153 */ getTag()154 public String getTag() { 155 if (tags.length > 0) { 156 return tags[0]; 157 } 158 return null; 159 } 160 } 161 162 public final static String RESULT_OK = null; 163 164 private final static Pattern sEmulatorRegexp = Pattern.compile(Device.RE_EMULATOR_SN); 165 private final static Pattern sVoiceStatusRegexp = Pattern.compile( 166 "gsm\\s+voice\\s+state:\\s*([a-z]+)", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$ 167 private final static Pattern sDataStatusRegexp = Pattern.compile( 168 "gsm\\s+data\\s+state:\\s*([a-z]+)", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$ 169 private final static Pattern sDownloadSpeedRegexp = Pattern.compile( 170 "\\s+download\\s+speed:\\s+(\\d+)\\s+bits.*", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$ 171 private final static Pattern sMinLatencyRegexp = Pattern.compile( 172 "\\s+minimum\\s+latency:\\s+(\\d+)\\s+ms", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$ 173 174 private final static HashMap<Integer, EmulatorConsole> sEmulators = 175 new HashMap<Integer, EmulatorConsole>(); 176 177 /** Gsm Status class */ 178 public static class GsmStatus { 179 /** Voice status. */ 180 public GsmMode voice = GsmMode.UNKNOWN; 181 /** Data status. */ 182 public GsmMode data = GsmMode.UNKNOWN; 183 } 184 185 /** Network Status class */ 186 public static class NetworkStatus { 187 /** network speed status. This is an index in the {@link #DOWNLOAD_SPEEDS} array. */ 188 public int speed = -1; 189 /** network latency status. This is an index in the {@link #MIN_LATENCIES} array. */ 190 public int latency = -1; 191 } 192 193 private int mPort; 194 195 private SocketChannel mSocketChannel; 196 197 private byte[] mBuffer = new byte[1024]; 198 199 /** 200 * Returns an {@link EmulatorConsole} object for the given {@link Device}. This can 201 * be an already existing console, or a new one if it hadn't been created yet. 202 * @param d The device that the console links to. 203 * @return an <code>EmulatorConsole</code> object or <code>null</code> if the connection failed. 204 */ getConsole(IDevice d)205 public static synchronized EmulatorConsole getConsole(IDevice d) { 206 // we need to make sure that the device is an emulator 207 Matcher m = sEmulatorRegexp.matcher(d.getSerialNumber()); 208 if (m.matches()) { 209 // get the port number. This is the console port. 210 int port; 211 try { 212 port = Integer.parseInt(m.group(1)); 213 if (port <= 0) { 214 return null; 215 } 216 } catch (NumberFormatException e) { 217 // looks like we failed to get the port number. This is a bit strange since 218 // it's coming from a regexp that only accept digit, but we handle the case 219 // and return null. 220 return null; 221 } 222 223 EmulatorConsole console = sEmulators.get(port); 224 225 if (console != null) { 226 // if the console exist, we ping the emulator to check the connection. 227 if (console.ping() == false) { 228 RemoveConsole(console.mPort); 229 console = null; 230 } 231 } 232 233 if (console == null) { 234 // no console object exists for this port so we create one, and start 235 // the connection. 236 console = new EmulatorConsole(port); 237 if (console.start()) { 238 sEmulators.put(port, console); 239 } else { 240 console = null; 241 } 242 } 243 244 return console; 245 } 246 247 return null; 248 } 249 250 /** 251 * Removes the console object associated with a port from the map. 252 * @param port The port of the console to remove. 253 */ RemoveConsole(int port)254 private static synchronized void RemoveConsole(int port) { 255 sEmulators.remove(port); 256 } 257 EmulatorConsole(int port)258 private EmulatorConsole(int port) { 259 super(); 260 mPort = port; 261 } 262 263 /** 264 * Starts the connection of the console. 265 * @return true if success. 266 */ start()267 private boolean start() { 268 269 InetSocketAddress socketAddr; 270 try { 271 InetAddress hostAddr = InetAddress.getByName(HOST); 272 socketAddr = new InetSocketAddress(hostAddr, mPort); 273 } catch (UnknownHostException e) { 274 return false; 275 } 276 277 try { 278 mSocketChannel = SocketChannel.open(socketAddr); 279 } catch (IOException e1) { 280 return false; 281 } 282 283 // read some stuff from it 284 readLines(); 285 286 return true; 287 } 288 289 /** 290 * Ping the emulator to check if the connection is still alive. 291 * @return true if the connection is alive. 292 */ ping()293 private synchronized boolean ping() { 294 // it looks like we can send stuff, even when the emulator quit, but we can't read 295 // from the socket. So we check the return of readLines() 296 if (sendCommand(COMMAND_PING)) { 297 return readLines() != null; 298 } 299 300 return false; 301 } 302 303 /** 304 * Sends a KILL command to the emulator. 305 */ kill()306 public synchronized void kill() { 307 if (sendCommand(COMMAND_KILL)) { 308 RemoveConsole(mPort); 309 } 310 } 311 getAvdName()312 public synchronized String getAvdName() { 313 if (sendCommand(COMMAND_AVD_NAME)) { 314 String[] result = readLines(); 315 if (result != null && result.length == 2) { // this should be the name on first line, 316 // and ok on 2nd line 317 return result[0]; 318 } else { 319 // try to see if there's a message after KO 320 Matcher m = RE_KO.matcher(result[result.length-1]); 321 if (m.matches()) { 322 return m.group(1); 323 } 324 } 325 } 326 327 return null; 328 } 329 330 /** 331 * Get the network status of the emulator. 332 * @return a {@link NetworkStatus} object containing the {@link GsmStatus}, or 333 * <code>null</code> if the query failed. 334 */ getNetworkStatus()335 public synchronized NetworkStatus getNetworkStatus() { 336 if (sendCommand(COMMAND_NETWORK_STATUS)) { 337 /* Result is in the format 338 Current network status: 339 download speed: 14400 bits/s (1.8 KB/s) 340 upload speed: 14400 bits/s (1.8 KB/s) 341 minimum latency: 0 ms 342 maximum latency: 0 ms 343 */ 344 String[] result = readLines(); 345 346 if (isValid(result)) { 347 // we only compare agains the min latency and the download speed 348 // let's not rely on the order of the output, and simply loop through 349 // the line testing the regexp. 350 NetworkStatus status = new NetworkStatus(); 351 for (String line : result) { 352 Matcher m = sDownloadSpeedRegexp.matcher(line); 353 if (m.matches()) { 354 // get the string value 355 String value = m.group(1); 356 357 // get the index from the list 358 status.speed = getSpeedIndex(value); 359 360 // move on to next line. 361 continue; 362 } 363 364 m = sMinLatencyRegexp.matcher(line); 365 if (m.matches()) { 366 // get the string value 367 String value = m.group(1); 368 369 // get the index from the list 370 status.latency = getLatencyIndex(value); 371 372 // move on to next line. 373 continue; 374 } 375 } 376 377 return status; 378 } 379 } 380 381 return null; 382 } 383 384 /** 385 * Returns the current gsm status of the emulator 386 * @return a {@link GsmStatus} object containing the gms status, or <code>null</code> 387 * if the query failed. 388 */ getGsmStatus()389 public synchronized GsmStatus getGsmStatus() { 390 if (sendCommand(COMMAND_GSM_STATUS)) { 391 /* 392 * result is in the format: 393 * gsm status 394 * gsm voice state: home 395 * gsm data state: home 396 */ 397 398 String[] result = readLines(); 399 if (isValid(result)) { 400 401 GsmStatus status = new GsmStatus(); 402 403 // let's not rely on the order of the output, and simply loop through 404 // the line testing the regexp. 405 for (String line : result) { 406 Matcher m = sVoiceStatusRegexp.matcher(line); 407 if (m.matches()) { 408 // get the string value 409 String value = m.group(1); 410 411 // get the index from the list 412 status.voice = GsmMode.getEnum(value.toLowerCase()); 413 414 // move on to next line. 415 continue; 416 } 417 418 m = sDataStatusRegexp.matcher(line); 419 if (m.matches()) { 420 // get the string value 421 String value = m.group(1); 422 423 // get the index from the list 424 status.data = GsmMode.getEnum(value.toLowerCase()); 425 426 // move on to next line. 427 continue; 428 } 429 } 430 431 return status; 432 } 433 } 434 435 return null; 436 } 437 438 /** 439 * Sets the GSM voice mode. 440 * @param mode the {@link GsmMode} value. 441 * @return RESULT_OK if success, an error String otherwise. 442 * @throws InvalidParameterException if mode is an invalid value. 443 */ setGsmVoiceMode(GsmMode mode)444 public synchronized String setGsmVoiceMode(GsmMode mode) throws InvalidParameterException { 445 if (mode == GsmMode.UNKNOWN) { 446 throw new InvalidParameterException(); 447 } 448 449 String command = String.format(COMMAND_GSM_VOICE, mode.getTag()); 450 return processCommand(command); 451 } 452 453 /** 454 * Sets the GSM data mode. 455 * @param mode the {@link GsmMode} value 456 * @return {@link #RESULT_OK} if success, an error String otherwise. 457 * @throws InvalidParameterException if mode is an invalid value. 458 */ setGsmDataMode(GsmMode mode)459 public synchronized String setGsmDataMode(GsmMode mode) throws InvalidParameterException { 460 if (mode == GsmMode.UNKNOWN) { 461 throw new InvalidParameterException(); 462 } 463 464 String command = String.format(COMMAND_GSM_DATA, mode.getTag()); 465 return processCommand(command); 466 } 467 468 /** 469 * Initiate an incoming call on the emulator. 470 * @param number a string representing the calling number. 471 * @return {@link #RESULT_OK} if success, an error String otherwise. 472 */ call(String number)473 public synchronized String call(String number) { 474 String command = String.format(COMMAND_GSM_CALL, number); 475 return processCommand(command); 476 } 477 478 /** 479 * Cancels a current call. 480 * @param number the number of the call to cancel 481 * @return {@link #RESULT_OK} if success, an error String otherwise. 482 */ cancelCall(String number)483 public synchronized String cancelCall(String number) { 484 String command = String.format(COMMAND_GSM_CANCEL_CALL, number); 485 return processCommand(command); 486 } 487 488 /** 489 * Sends an SMS to the emulator 490 * @param number The sender phone number 491 * @param message The SMS message. \ characters must be escaped. The carriage return is 492 * the 2 character sequence {'\', 'n' } 493 * 494 * @return {@link #RESULT_OK} if success, an error String otherwise. 495 */ sendSms(String number, String message)496 public synchronized String sendSms(String number, String message) { 497 String command = String.format(COMMAND_SMS_SEND, number, message); 498 return processCommand(command); 499 } 500 501 /** 502 * Sets the network speed. 503 * @param selectionIndex The index in the {@link #NETWORK_SPEEDS} table. 504 * @return {@link #RESULT_OK} if success, an error String otherwise. 505 */ setNetworkSpeed(int selectionIndex)506 public synchronized String setNetworkSpeed(int selectionIndex) { 507 String command = String.format(COMMAND_NETWORK_SPEED, NETWORK_SPEEDS[selectionIndex]); 508 return processCommand(command); 509 } 510 511 /** 512 * Sets the network latency. 513 * @param selectionIndex The index in the {@link #NETWORK_LATENCIES} table. 514 * @return {@link #RESULT_OK} if success, an error String otherwise. 515 */ setNetworkLatency(int selectionIndex)516 public synchronized String setNetworkLatency(int selectionIndex) { 517 String command = String.format(COMMAND_NETWORK_LATENCY, NETWORK_LATENCIES[selectionIndex]); 518 return processCommand(command); 519 } 520 sendLocation(double longitude, double latitude, double elevation)521 public synchronized String sendLocation(double longitude, double latitude, double elevation) { 522 523 Calendar c = Calendar.getInstance(); 524 525 double absLong = Math.abs(longitude); 526 int longDegree = (int)Math.floor(absLong); 527 char longDirection = 'E'; 528 if (longitude < 0) { 529 longDirection = 'W'; 530 } 531 532 double longMinute = (absLong - Math.floor(absLong)) * 60; 533 534 double absLat = Math.abs(latitude); 535 int latDegree = (int)Math.floor(absLat); 536 char latDirection = 'N'; 537 if (latitude < 0) { 538 latDirection = 'S'; 539 } 540 541 double latMinute = (absLat - Math.floor(absLat)) * 60; 542 543 String command = String.format(COMMAND_GPS, 544 c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), 545 c.get(Calendar.SECOND), c.get(Calendar.MILLISECOND), 546 latDegree, latMinute, latDirection, 547 longDegree, longMinute, longDirection); 548 549 return processCommand(command); 550 } 551 552 /** 553 * Sends a command to the emulator console. 554 * @param command The command string. <b>MUST BE TERMINATED BY \n</b>. 555 * @return true if success 556 */ sendCommand(String command)557 private boolean sendCommand(String command) { 558 boolean result = false; 559 try { 560 byte[] bCommand; 561 try { 562 bCommand = command.getBytes(DEFAULT_ENCODING); 563 } catch (UnsupportedEncodingException e) { 564 // wrong encoding... 565 return result; 566 } 567 568 // write the command 569 AdbHelper.write(mSocketChannel, bCommand, bCommand.length, DdmPreferences.getTimeOut()); 570 571 result = true; 572 } catch (IOException e) { 573 return false; 574 } finally { 575 if (result == false) { 576 // FIXME connection failed somehow, we need to disconnect the console. 577 RemoveConsole(mPort); 578 } 579 } 580 581 return result; 582 } 583 584 /** 585 * Sends a command to the emulator and parses its answer. 586 * @param command the command to send. 587 * @return {@link #RESULT_OK} if success, an error message otherwise. 588 */ processCommand(String command)589 private String processCommand(String command) { 590 if (sendCommand(command)) { 591 String[] result = readLines(); 592 593 if (result != null && result.length > 0) { 594 Matcher m = RE_KO.matcher(result[result.length-1]); 595 if (m.matches()) { 596 return m.group(1); 597 } 598 return RESULT_OK; 599 } 600 601 return "Unable to communicate with the emulator"; 602 } 603 604 return "Unable to send command to the emulator"; 605 } 606 607 /** 608 * Reads line from the console socket. This call is blocking until we read the lines: 609 * <ul> 610 * <li>OK\r\n</li> 611 * <li>KO<msg>\r\n</li> 612 * </ul> 613 * @return the array of strings read from the emulator. 614 */ readLines()615 private String[] readLines() { 616 try { 617 ByteBuffer buf = ByteBuffer.wrap(mBuffer, 0, mBuffer.length); 618 int numWaits = 0; 619 boolean stop = false; 620 621 while (buf.position() != buf.limit() && stop == false) { 622 int count; 623 624 count = mSocketChannel.read(buf); 625 if (count < 0) { 626 return null; 627 } else if (count == 0) { 628 if (numWaits * WAIT_TIME > STD_TIMEOUT) { 629 return null; 630 } 631 // non-blocking spin 632 try { 633 Thread.sleep(WAIT_TIME); 634 } catch (InterruptedException ie) { 635 } 636 numWaits++; 637 } else { 638 numWaits = 0; 639 } 640 641 // check the last few char aren't OK. For a valid message to test 642 // we need at least 4 bytes (OK/KO + \r\n) 643 if (buf.position() >= 4) { 644 int pos = buf.position(); 645 if (endsWithOK(pos) || lastLineIsKO(pos)) { 646 stop = true; 647 } 648 } 649 } 650 651 String msg = new String(mBuffer, 0, buf.position(), DEFAULT_ENCODING); 652 return msg.split("\r\n"); //$NON-NLS-1$ 653 } catch (IOException e) { 654 return null; 655 } 656 } 657 658 /** 659 * Returns true if the 4 characters *before* the current position are "OK\r\n" 660 * @param currentPosition The current position 661 */ endsWithOK(int currentPosition)662 private boolean endsWithOK(int currentPosition) { 663 if (mBuffer[currentPosition-1] == '\n' && 664 mBuffer[currentPosition-2] == '\r' && 665 mBuffer[currentPosition-3] == 'K' && 666 mBuffer[currentPosition-4] == 'O') { 667 return true; 668 } 669 670 return false; 671 } 672 673 /** 674 * Returns true if the last line starts with KO and is also terminated by \r\n 675 * @param currentPosition the current position 676 */ lastLineIsKO(int currentPosition)677 private boolean lastLineIsKO(int currentPosition) { 678 // first check that the last 2 characters are CRLF 679 if (mBuffer[currentPosition-1] != '\n' || 680 mBuffer[currentPosition-2] != '\r') { 681 return false; 682 } 683 684 // now loop backward looking for the previous CRLF, or the beginning of the buffer 685 int i = 0; 686 for (i = currentPosition-3 ; i >= 0; i--) { 687 if (mBuffer[i] == '\n') { 688 // found \n! 689 if (i > 0 && mBuffer[i-1] == '\r') { 690 // found \r! 691 break; 692 } 693 } 694 } 695 696 // here it is either -1 if we reached the start of the buffer without finding 697 // a CRLF, or the position of \n. So in both case we look at the characters at i+1 and i+2 698 if (mBuffer[i+1] == 'K' && mBuffer[i+2] == 'O') { 699 // found error! 700 return true; 701 } 702 703 return false; 704 } 705 706 /** 707 * Returns true if the last line of the result does not start with KO 708 */ isValid(String[] result)709 private boolean isValid(String[] result) { 710 if (result != null && result.length > 0) { 711 return !(RE_KO.matcher(result[result.length-1]).matches()); 712 } 713 return false; 714 } 715 getLatencyIndex(String value)716 private int getLatencyIndex(String value) { 717 try { 718 // get the int value 719 int latency = Integer.parseInt(value); 720 721 // check for the speed from the index 722 for (int i = 0 ; i < MIN_LATENCIES.length; i++) { 723 if (MIN_LATENCIES[i] == latency) { 724 return i; 725 } 726 } 727 } catch (NumberFormatException e) { 728 // Do nothing, we'll just return -1. 729 } 730 731 return -1; 732 } 733 getSpeedIndex(String value)734 private int getSpeedIndex(String value) { 735 try { 736 // get the int value 737 int speed = Integer.parseInt(value); 738 739 // check for the speed from the index 740 for (int i = 0 ; i < DOWNLOAD_SPEEDS.length; i++) { 741 if (DOWNLOAD_SPEEDS[i] == speed) { 742 return i; 743 } 744 } 745 } catch (NumberFormatException e) { 746 // Do nothing, we'll just return -1. 747 } 748 749 return -1; 750 } 751 } 752