• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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.sdkmanager;
18 
19 import com.android.prefs.AndroidLocation;
20 import com.android.prefs.AndroidLocation.AndroidLocationException;
21 import com.android.sdklib.IAndroidTarget;
22 import com.android.sdklib.ISdkLog;
23 import com.android.sdklib.SdkConstants;
24 import com.android.sdklib.SdkManager;
25 import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
26 import com.android.sdklib.internal.avd.AvdManager;
27 import com.android.sdklib.internal.avd.HardwareProperties;
28 import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
29 import com.android.sdklib.internal.avd.HardwareProperties.HardwareProperty;
30 import com.android.sdklib.internal.project.ProjectCreator;
31 import com.android.sdklib.internal.project.ProjectProperties;
32 import com.android.sdklib.internal.project.ProjectCreator.OutputLevel;
33 import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
34 import com.android.sdklib.repository.SdkRepository;
35 import com.android.sdklib.xml.AndroidXPathFactory;
36 import com.android.sdkmanager.internal.repository.AboutPage;
37 import com.android.sdkmanager.internal.repository.SettingsPage;
38 import com.android.sdkuilib.internal.repository.UpdateNoWindow;
39 import com.android.sdkuilib.internal.repository.LocalPackagesPage;
40 import com.android.sdkuilib.internal.widgets.MessageBoxLog;
41 import com.android.sdkuilib.repository.UpdaterWindow;
42 
43 import org.eclipse.swt.widgets.Display;
44 import org.xml.sax.InputSource;
45 
46 import java.io.File;
47 import java.io.FileInputStream;
48 import java.io.FileNotFoundException;
49 import java.io.IOException;
50 import java.util.ArrayList;
51 import java.util.Arrays;
52 import java.util.HashMap;
53 import java.util.Map;
54 
55 import javax.xml.xpath.XPath;
56 import javax.xml.xpath.XPathExpressionException;
57 
58 /**
59  * Main class for the 'android' application.
60  */
61 public class Main {
62 
63     /** Java property that defines the location of the sdk/tools directory. */
64     public final static String TOOLSDIR = "com.android.sdkmanager.toolsdir";
65     /** Java property that defines the working directory. On Windows the current working directory
66      *  is actually the tools dir, in which case this is used to get the original CWD. */
67     private final static String WORKDIR = "com.android.sdkmanager.workdir";
68 
69     /** Value returned by {@link #resolveTargetName(String)} when the target id does not match. */
70     private final static int INVALID_TARGET_ID = 0;
71 
72     private final static String[] BOOLEAN_YES_REPLIES = new String[] { "yes", "y" };
73     private final static String[] BOOLEAN_NO_REPLIES = new String[] { "no", "n" };
74 
75     /** Path to the SDK folder. This is the parent of {@link #TOOLSDIR}. */
76     private String mOsSdkFolder;
77     /** Logger object. Use this to print normal output, warnings or errors. */
78     private ISdkLog mSdkLog;
79     /** The SDK manager parses the SDK folder and gives access to the content. */
80     private SdkManager mSdkManager;
81     /** Command-line processor with options specific to SdkManager. */
82     private SdkCommandLine mSdkCommandLine;
83     /** The working directory, either null or set to an existing absolute canonical directory. */
84     private File mWorkDir;
85 
main(String[] args)86     public static void main(String[] args) {
87         new Main().run(args);
88     }
89 
90     /**
91      * Runs the sdk manager app
92      */
run(String[] args)93     private void run(String[] args) {
94         createLogger();
95         init();
96         mSdkCommandLine.parseArgs(args);
97         parseSdk();
98         doAction();
99     }
100 
101     /**
102      * Creates the {@link #mSdkLog} object.
103      * This must be done before {@link #init()} as it will be used to report errors.
104      * This logger prints to the attached console.
105      */
createLogger()106     private void createLogger() {
107         mSdkLog = new ISdkLog() {
108             public void error(Throwable t, String errorFormat, Object... args) {
109                 if (errorFormat != null) {
110                     System.err.printf("Error: " + errorFormat, args);
111                     if (!errorFormat.endsWith("\n")) {
112                         System.err.printf("\n");
113                     }
114                 }
115                 if (t != null) {
116                     System.err.printf("Error: %s\n", t.getMessage());
117                 }
118             }
119 
120             public void warning(String warningFormat, Object... args) {
121                 if (mSdkCommandLine.isVerbose()) {
122                     System.out.printf("Warning: " + warningFormat, args);
123                     if (!warningFormat.endsWith("\n")) {
124                         System.out.printf("\n");
125                     }
126                 }
127             }
128 
129             public void printf(String msgFormat, Object... args) {
130                 System.out.printf(msgFormat, args);
131             }
132         };
133     }
134 
135     /**
136      * Init the application by making sure the SDK path is available and
137      * doing basic parsing of the SDK.
138      */
init()139     private void init() {
140         mSdkCommandLine = new SdkCommandLine(mSdkLog);
141 
142         // We get passed a property for the tools dir
143         String toolsDirProp = System.getProperty(TOOLSDIR);
144         if (toolsDirProp == null) {
145             // for debugging, it's easier to override using the process environment
146             toolsDirProp = System.getenv(TOOLSDIR);
147         }
148 
149         if (toolsDirProp != null) {
150             // got back a level for the SDK folder
151             File tools;
152             if (toolsDirProp.length() > 0) {
153                 tools = new File(toolsDirProp);
154                 mOsSdkFolder = tools.getParent();
155             } else {
156                 try {
157                     tools = new File(".").getCanonicalFile();
158                     mOsSdkFolder = tools.getParent();
159                 } catch (IOException e) {
160                     // Will print an error below since mSdkFolder is not defined
161                 }
162             }
163         }
164 
165         if (mOsSdkFolder == null) {
166             errorAndExit("The tools directory property is not set, please make sure you are executing %1$s",
167                 SdkConstants.androidCmdName());
168         }
169 
170         // We might get passed a property for the working directory
171         // Either it is a valid directory and mWorkDir is set to it's absolute canonical value
172         // or mWorkDir remains null.
173         String workDirProp = System.getProperty(WORKDIR);
174         if (workDirProp == null) {
175             workDirProp = System.getenv(WORKDIR);
176         }
177         if (workDirProp != null) {
178             // This should be a valid directory
179             mWorkDir = new File(workDirProp);
180             try {
181                 mWorkDir = mWorkDir.getCanonicalFile().getAbsoluteFile();
182             } catch (IOException e) {
183                 mWorkDir = null;
184             }
185             if (mWorkDir == null || !mWorkDir.isDirectory()) {
186                 errorAndExit("The working directory does not seem to be valid: '%1$s", workDirProp);
187             }
188         }
189     }
190 
191     /**
192      * Does the basic SDK parsing required for all actions
193      */
parseSdk()194     private void parseSdk() {
195         mSdkManager = SdkManager.createManager(mOsSdkFolder, mSdkLog);
196 
197         if (mSdkManager == null) {
198             errorAndExit("Unable to parse SDK content.");
199         }
200     }
201 
202     /**
203      * Actually do an action...
204      */
doAction()205     private void doAction() {
206         String verb = mSdkCommandLine.getVerb();
207         String directObject = mSdkCommandLine.getDirectObject();
208 
209         if (SdkCommandLine.VERB_LIST.equals(verb)) {
210             // list action.
211             if (SdkCommandLine.OBJECT_TARGET.equals(directObject)) {
212                 displayTargetList();
213 
214             } else if (SdkCommandLine.OBJECT_AVD.equals(directObject)) {
215                 displayAvdList();
216 
217             } else {
218                 displayTargetList();
219                 displayAvdList();
220             }
221 
222         } else if (SdkCommandLine.VERB_CREATE.equals(verb)) {
223             if (SdkCommandLine.OBJECT_AVD.equals(directObject)) {
224                 createAvd();
225 
226             } else if (SdkCommandLine.OBJECT_PROJECT.equals(directObject)) {
227                 createProject(false /*library*/);
228 
229             } else if (SdkCommandLine.OBJECT_TEST_PROJECT.equals(directObject)) {
230                 createTestProject();
231 
232             } else if (SdkCommandLine.OBJECT_LIB_PROJECT.equals(directObject)) {
233                 createProject(true /*library*/);
234 
235             } else if (SdkCommandLine.OBJECT_EXPORT_PROJECT.equals(directObject)) {
236                 createExportProject();
237 
238             }
239         } else if (SdkCommandLine.VERB_UPDATE.equals(verb)) {
240             if (SdkCommandLine.OBJECT_AVD.equals(directObject)) {
241                 updateAvd();
242 
243             } else if (SdkCommandLine.OBJECT_PROJECT.equals(directObject)) {
244                 updateProject(false /*library*/);
245 
246             } else if (SdkCommandLine.OBJECT_TEST_PROJECT.equals(directObject)) {
247                 updateTestProject();
248 
249             } else if (SdkCommandLine.OBJECT_LIB_PROJECT.equals(directObject)) {
250                 updateProject(true /*library*/);
251 
252             } else if (SdkCommandLine.OBJECT_EXPORT_PROJECT.equals(directObject)) {
253                 updateExportProject();
254 
255             } else if (SdkCommandLine.OBJECT_SDK.equals(directObject)) {
256                 if (mSdkCommandLine.getFlagNoUI()) {
257                     updateSdkNoUI();
258                 } else {
259                     showMainWindow(true /*autoUpdate*/);
260                 }
261 
262             } else if (SdkCommandLine.OBJECT_ADB.equals(directObject)) {
263                 updateAdb();
264 
265             }
266         } else if (SdkCommandLine.VERB_DELETE.equals(verb) &&
267                 SdkCommandLine.OBJECT_AVD.equals(directObject)) {
268             deleteAvd();
269 
270         } else if (SdkCommandLine.VERB_MOVE.equals(verb) &&
271                 SdkCommandLine.OBJECT_AVD.equals(directObject)) {
272             moveAvd();
273 
274         } else if (verb == null && directObject == null) {
275             showMainWindow(false /*autoUpdate*/);
276 
277         } else {
278             mSdkCommandLine.printHelpAndExit(null);
279         }
280     }
281 
282     /**
283      * Display the main SdkManager app window
284      */
showMainWindow(boolean autoUpdate)285     private void showMainWindow(boolean autoUpdate) {
286         try {
287             // display a message talking about the command line version
288             System.out.printf("No command line parameters provided, launching UI.\n" +
289                     "See 'android --help' for operations from the command line.\n");
290 
291             MessageBoxLog errorLogger = new MessageBoxLog(
292                     "SDK Manager",
293                     Display.getCurrent(),
294                     true /*logErrorsOnly*/);
295 
296             UpdaterWindow window = new UpdaterWindow(
297                     null /* parentShell */,
298                     errorLogger,
299                     mOsSdkFolder,
300                     false /*userCanChangeSdkRoot*/);
301             window.registerPage("Settings", SettingsPage.class);
302             window.registerPage("About", AboutPage.class);
303             if (autoUpdate) {
304                 window.setInitialPage(LocalPackagesPage.class);
305                 window.setRequestAutoUpdate(true);
306             }
307             window.open();
308 
309             errorLogger.displayResult(true);
310 
311         } catch (Exception e) {
312             e.printStackTrace();
313         }
314     }
315 
316     /**
317      * Updates the whole SDK without any UI, just using console output.
318      */
updateSdkNoUI()319     private void updateSdkNoUI() {
320         boolean force = mSdkCommandLine.getFlagForce();
321         boolean useHttp = mSdkCommandLine.getFlagNoHttps();
322         boolean dryMode = mSdkCommandLine.getFlagDryMode();
323         boolean obsolete = mSdkCommandLine.getFlagObsolete();
324 
325         // Check filter types.
326         ArrayList<String> pkgFilter = new ArrayList<String>();
327         String filter = mSdkCommandLine.getParamFilter();
328         if (filter != null && filter.length() > 0) {
329             for (String t : filter.split(",")) {    //$NON-NLS-1$
330                 if (t != null) {
331                     t = t.trim();
332                     if (t.length() > 0) {
333                         boolean found = false;
334                         for (String t2 : SdkRepository.NODES) {
335                             if (t2.equals(t)) {
336                                 pkgFilter.add(t2);
337                                 found = true;
338                                 break;
339                             }
340                         }
341                         if (!found) {
342                             errorAndExit(
343                                 "Unknown package filter type '%1$s'.\nAccepted values are: %2$s",
344                                 t,
345                                 Arrays.toString(SdkRepository.NODES));
346                             return;
347                         }
348                     }
349                 }
350             }
351         }
352 
353         UpdateNoWindow upd = new UpdateNoWindow(mOsSdkFolder, mSdkManager, mSdkLog, force, useHttp);
354         upd.updateAll(pkgFilter, obsolete, dryMode);
355     }
356 
357     /**
358      * Returns a configured {@link ProjectCreator} instance.
359      */
getProjectCreator()360     private ProjectCreator getProjectCreator() {
361         ProjectCreator creator = new ProjectCreator(mSdkManager, mOsSdkFolder,
362                 mSdkCommandLine.isVerbose() ? OutputLevel.VERBOSE :
363                     mSdkCommandLine.isSilent() ? OutputLevel.SILENT :
364                         OutputLevel.NORMAL,
365                 mSdkLog);
366         return creator;
367     }
368 
369 
370     /**
371      * Creates a new Android project based on command-line parameters
372      */
createProject(boolean library)373     private void createProject(boolean library) {
374         String directObject = library? SdkCommandLine.OBJECT_LIB_PROJECT :
375                 SdkCommandLine.OBJECT_PROJECT;
376 
377         // get the target and try to resolve it.
378         int targetId = resolveTargetName(mSdkCommandLine.getParamTargetId());
379         IAndroidTarget[] targets = mSdkManager.getTargets();
380         if (targetId == INVALID_TARGET_ID || targetId > targets.length) {
381             errorAndExit("Target id is not valid. Use '%s list targets' to get the target ids.",
382                     SdkConstants.androidCmdName());
383         }
384         IAndroidTarget target = targets[targetId - 1];  // target id is 1-based
385 
386         ProjectCreator creator = getProjectCreator();
387 
388         String projectDir = getProjectLocation(mSdkCommandLine.getParamLocationPath());
389 
390         String projectName = mSdkCommandLine.getParamName();
391         String packageName = mSdkCommandLine.getParamProjectPackage(directObject);
392         String activityName = null;
393         if (library == false) {
394             activityName = mSdkCommandLine.getParamProjectActivity();
395         }
396 
397         if (projectName != null &&
398                 !ProjectCreator.RE_PROJECT_NAME.matcher(projectName).matches()) {
399             errorAndExit(
400                 "Project name '%1$s' contains invalid characters.\nAllowed characters are: %2$s",
401                 projectName, ProjectCreator.CHARS_PROJECT_NAME);
402             return;
403         }
404 
405         if (activityName != null &&
406                 !ProjectCreator.RE_ACTIVITY_NAME.matcher(activityName).matches()) {
407             errorAndExit(
408                 "Activity name '%1$s' contains invalid characters.\nAllowed characters are: %2$s",
409                 activityName, ProjectCreator.CHARS_ACTIVITY_NAME);
410             return;
411         }
412 
413         if (packageName != null &&
414                 !ProjectCreator.RE_PACKAGE_NAME.matcher(packageName).matches()) {
415             errorAndExit(
416                 "Package name '%1$s' contains invalid characters.\n" +
417                 "A package name must be constitued of two Java identifiers.\n" +
418                 "Each identifier allowed characters are: %2$s",
419                 packageName, ProjectCreator.CHARS_PACKAGE_NAME);
420             return;
421         }
422 
423         creator.createProject(projectDir,
424                 projectName,
425                 packageName,
426                 activityName,
427                 target,
428                 library,
429                 null /*pathToMain*/);
430     }
431 
432     /**
433      * Creates a new Android test project based on command-line parameters
434      */
createTestProject()435     private void createTestProject() {
436 
437         String projectDir = getProjectLocation(mSdkCommandLine.getParamLocationPath());
438 
439         // first check the path of the parent project, and make sure it's valid.
440         String pathToMainProject = mSdkCommandLine.getParamTestProjectMain();
441 
442         File parentProject = new File(pathToMainProject);
443         if (parentProject.isAbsolute() == false) {
444             // if the path is not absolute, we need to resolve it based on the
445             // destination path of the project
446             try {
447                 parentProject = new File(projectDir, pathToMainProject).getCanonicalFile();
448             } catch (IOException e) {
449                 errorAndExit("Unable to resolve Main project's directory: %1$s",
450                         pathToMainProject);
451                 return; // help Eclipse static analyzer understand we'll never execute the rest.
452             }
453         }
454 
455         if (parentProject.isDirectory() == false) {
456             errorAndExit("Main project's directory does not exist: %1$s",
457                     pathToMainProject);
458             return;
459         }
460 
461         // now look for a manifest in there
462         File manifest = new File(parentProject, SdkConstants.FN_ANDROID_MANIFEST_XML);
463         if (manifest.isFile() == false) {
464             errorAndExit("No AndroidManifest.xml file found in the main project directory: %1$s",
465                     parentProject.getAbsolutePath());
466             return;
467         }
468 
469         // now query the manifest for the package file.
470         XPath xpath = AndroidXPathFactory.newXPath();
471         String packageName, activityName;
472 
473         try {
474             packageName = xpath.evaluate("/manifest/@package",
475                     new InputSource(new FileInputStream(manifest)));
476 
477             mSdkLog.printf("Found main project package: %1$s\n", packageName);
478 
479             // now get the name of the first activity we find
480             activityName = xpath.evaluate("/manifest/application/activity[1]/@android:name",
481                     new InputSource(new FileInputStream(manifest)));
482             // xpath will return empty string when there's no match
483             if (activityName == null || activityName.length() == 0) {
484                 activityName = null;
485             } else {
486                 mSdkLog.printf("Found main project activity: %1$s\n", activityName);
487             }
488         } catch (FileNotFoundException e) {
489             // this shouldn't happen as we test it above.
490             errorAndExit("No AndroidManifest.xml file found in main project.");
491             return; // this is not strictly needed because errorAndExit will stop the execution,
492             // but this makes the java compiler happy, wrt to uninitialized variables.
493         } catch (XPathExpressionException e) {
494             // looks like the main manifest is not valid.
495             errorAndExit("Unable to parse main project manifest to get information.");
496             return; // this is not strictly needed because errorAndExit will stop the execution,
497                     // but this makes the java compiler happy, wrt to uninitialized variables.
498         }
499 
500         // now get the target hash
501         ProjectProperties p = ProjectProperties.load(parentProject.getAbsolutePath(),
502                 PropertyType.DEFAULT);
503         if (p == null) {
504             errorAndExit("Unable to load the main project's %1$s",
505                     PropertyType.DEFAULT.getFilename());
506             return;
507         }
508 
509         String targetHash = p.getProperty(ProjectProperties.PROPERTY_TARGET);
510         if (targetHash == null) {
511             errorAndExit("Couldn't find the main project target");
512             return;
513         }
514 
515         // and resolve it.
516         IAndroidTarget target = mSdkManager.getTargetFromHashString(targetHash);
517         if (target == null) {
518             errorAndExit(
519                     "Unable to resolve main project target '%1$s'. You may want to install the platform in your SDK.",
520                     targetHash);
521             return;
522         }
523 
524         mSdkLog.printf("Found main project target: %1$s\n", target.getFullName());
525 
526         ProjectCreator creator = getProjectCreator();
527 
528         String projectName = mSdkCommandLine.getParamName();
529 
530         if (projectName != null &&
531                 !ProjectCreator.RE_PROJECT_NAME.matcher(projectName).matches()) {
532             errorAndExit(
533                 "Project name '%1$s' contains invalid characters.\nAllowed characters are: %2$s",
534                 projectName, ProjectCreator.CHARS_PROJECT_NAME);
535             return;
536         }
537 
538         creator.createProject(projectDir,
539                 projectName,
540                 packageName,
541                 activityName,
542                 target,
543                 false /* library*/,
544                 pathToMainProject);
545     }
546 
547     /**
548      * Creates a new Android Export project based on command-line parameters
549      */
createExportProject()550     private void createExportProject() {
551         ProjectCreator creator = getProjectCreator();
552 
553         String projectDir = getProjectLocation(mSdkCommandLine.getParamLocationPath());
554 
555         String projectName = mSdkCommandLine.getParamName();
556         String packageName = mSdkCommandLine.getParamProjectPackage(
557                 SdkCommandLine.OBJECT_EXPORT_PROJECT);
558 
559         if (projectName != null &&
560                 !ProjectCreator.RE_PROJECT_NAME.matcher(projectName).matches()) {
561             errorAndExit(
562                 "Project name '%1$s' contains invalid characters.\nAllowed characters are: %2$s",
563                 projectName, ProjectCreator.CHARS_PROJECT_NAME);
564             return;
565         }
566 
567         if (packageName != null &&
568                 !ProjectCreator.RE_PACKAGE_NAME.matcher(packageName).matches()) {
569             errorAndExit(
570                 "Package name '%1$s' contains invalid characters.\n" +
571                 "A package name must be constitued of two Java identifiers.\n" +
572                 "Each identifier allowed characters are: %2$s",
573                 packageName, ProjectCreator.CHARS_PACKAGE_NAME);
574             return;
575         }
576 
577         creator.createExportProject(projectDir, projectName, packageName);
578     }
579 
580     /**
581      * Updates an existing Android project based on command-line parameters
582      * @param library whether the project is a library project.
583      */
updateProject(boolean library)584     private void updateProject(boolean library) {
585         // get the target and try to resolve it.
586         IAndroidTarget target = null;
587         String targetStr = mSdkCommandLine.getParamTargetId();
588         // For "update project" the target parameter is optional so having null is acceptable.
589         // However if there's a value, it must be valid.
590         if (targetStr != null) {
591             IAndroidTarget[] targets = mSdkManager.getTargets();
592             int targetId = resolveTargetName(targetStr);
593             if (targetId == INVALID_TARGET_ID || targetId > targets.length) {
594                 errorAndExit("Target id '%1$s' is not valid. Use '%2$s list targets' to get the target ids.",
595                         targetStr,
596                         SdkConstants.androidCmdName());
597             }
598             target = targets[targetId - 1];  // target id is 1-based
599         }
600 
601         ProjectCreator creator = getProjectCreator();
602 
603         String projectDir = getProjectLocation(mSdkCommandLine.getParamLocationPath());
604 
605         String libraryPath = library ? null :
606             mSdkCommandLine.getParamProjectLibrary(SdkCommandLine.OBJECT_PROJECT);
607 
608         creator.updateProject(projectDir,
609                 target,
610                 mSdkCommandLine.getParamName(),
611                 libraryPath);
612 
613         if (library == false) {
614             boolean doSubProjects = mSdkCommandLine.getParamSubProject();
615             boolean couldHaveDone = false;
616 
617             // If there are any sub-folders with a manifest, try to update them as projects
618             // too. This will take care of updating any underlying test project even if the
619             // user changed the folder name.
620             File[] files = new File(projectDir).listFiles();
621             if (files != null) {
622                 for (File dir : files) {
623                     if (dir.isDirectory() &&
624                             new File(dir, SdkConstants.FN_ANDROID_MANIFEST_XML).isFile()) {
625                         if (doSubProjects) {
626                             creator.updateProject(dir.getPath(),
627                                     target,
628                                     mSdkCommandLine.getParamName(),
629                                     null /*libraryPath*/);
630                         } else {
631                             couldHaveDone = true;
632                         }
633                     }
634                 }
635             }
636 
637             if (couldHaveDone) {
638                 mSdkLog.printf(
639                         "It seems that there are sub-projects. If you want to update them\nplease use the --%1$s parameter.\n",
640                         SdkCommandLine.KEY_SUBPROJECTS);
641             }
642         }
643     }
644 
645     /**
646      * Updates an existing test project with a new path to the main project.
647      */
updateTestProject()648     private void updateTestProject() {
649         ProjectCreator creator = getProjectCreator();
650 
651         String projectDir = getProjectLocation(mSdkCommandLine.getParamLocationPath());
652 
653         creator.updateTestProject(projectDir, mSdkCommandLine.getParamTestProjectMain(),
654                 mSdkManager);
655     }
656 
657     /**
658      * Updates an existing Android export project based on command-line parameters
659      */
updateExportProject()660     private void updateExportProject() {
661         ProjectCreator creator = getProjectCreator();
662 
663         String projectDir = getProjectLocation(mSdkCommandLine.getParamLocationPath());
664 
665         String projectName = mSdkCommandLine.getParamName();
666         if (projectName != null &&
667                 !ProjectCreator.RE_PROJECT_NAME.matcher(projectName).matches()) {
668             errorAndExit(
669                 "Project name '%1$s' contains invalid characters.\nAllowed characters are: %2$s",
670                 projectName, ProjectCreator.CHARS_PROJECT_NAME);
671             return;
672         }
673 
674         creator.updateExportProject(projectDir, projectName, mSdkCommandLine.getFlagForce());
675     }
676 
677     /**
678      * Adjusts the project location to make it absolute & canonical relative to the
679      * working directory, if any.
680      *
681      * @return The project absolute path relative to {@link #mWorkDir} or the original
682      *         newProjectLocation otherwise.
683      */
getProjectLocation(String newProjectLocation)684     private String getProjectLocation(String newProjectLocation) {
685 
686         // If the new project location is absolute, use it as-is
687         File projectDir = new File(newProjectLocation);
688         if (projectDir.isAbsolute()) {
689             return newProjectLocation;
690         }
691 
692         // if there's no working directory, just use the project location as-is.
693         if (mWorkDir == null) {
694             return newProjectLocation;
695         }
696 
697         // Combine then and get an absolute canonical directory
698         try {
699             projectDir = new File(mWorkDir, newProjectLocation).getCanonicalFile();
700 
701             return projectDir.getPath();
702         } catch (IOException e) {
703             errorAndExit("Failed to combine working directory '%1$s' with project location '%2$s': %3$s",
704                     mWorkDir.getPath(),
705                     newProjectLocation,
706                     e.getMessage());
707             return null;
708         }
709     }
710 
711     /**
712      * Displays the list of available Targets (Platforms and Add-ons)
713      */
displayTargetList()714     private void displayTargetList() {
715         mSdkLog.printf("Available Android targets:\n");
716 
717         int index = 1;
718         for (IAndroidTarget target : mSdkManager.getTargets()) {
719             mSdkLog.printf("id: %1$d or \"%2$s\"\n", index, target.hashString());
720             mSdkLog.printf("     Name: %s\n", target.getName());
721             if (target.isPlatform()) {
722                 mSdkLog.printf("     Type: Platform\n");
723                 mSdkLog.printf("     API level: %s\n", target.getVersion().getApiString());
724                 mSdkLog.printf("     Revision: %d\n", target.getRevision());
725             } else {
726                 mSdkLog.printf("     Type: Add-On\n");
727                 mSdkLog.printf("     Vendor: %s\n", target.getVendor());
728                 mSdkLog.printf("     Revision: %d\n", target.getRevision());
729                 if (target.getDescription() != null) {
730                     mSdkLog.printf("     Description: %s\n", target.getDescription());
731                 }
732                 mSdkLog.printf("     Based on Android %s (API level %s)\n",
733                         target.getVersionName(), target.getVersion().getApiString());
734 
735                 // display the optional libraries.
736                 IOptionalLibrary[] libraries = target.getOptionalLibraries();
737                 if (libraries != null) {
738                     mSdkLog.printf("     Libraries:\n");
739                     for (IOptionalLibrary library : libraries) {
740                         mSdkLog.printf("      * %1$s (%2$s)\n",
741                                 library.getName(), library.getJarName());
742                         mSdkLog.printf(String.format(
743                                 "          %1$s\n", library.getDescription()));
744                     }
745                 }
746             }
747 
748             // get the target skins
749             displaySkinList(target, "     Skins: ");
750 
751             if (target.getUsbVendorId() != IAndroidTarget.NO_USB_ID) {
752                 mSdkLog.printf("     Adds USB support for devices (Vendor: 0x%04X)\n",
753                         target.getUsbVendorId());
754             }
755 
756             index++;
757         }
758     }
759 
760     /**
761      * Displays the skins valid for the given target.
762      */
displaySkinList(IAndroidTarget target, String message)763     private void displaySkinList(IAndroidTarget target, String message) {
764         String[] skins = target.getSkins();
765         String defaultSkin = target.getDefaultSkin();
766         mSdkLog.printf(message);
767         if (skins != null) {
768             boolean first = true;
769             for (String skin : skins) {
770                 if (first == false) {
771                     mSdkLog.printf(", ");
772                 } else {
773                     first = false;
774                 }
775                 mSdkLog.printf(skin);
776 
777                 if (skin.equals(defaultSkin)) {
778                     mSdkLog.printf(" (default)");
779                 }
780             }
781             mSdkLog.printf("\n");
782         } else {
783             mSdkLog.printf("no skins.\n");
784         }
785     }
786 
787     /**
788      * Displays the list of available AVDs.
789      */
displayAvdList()790     private void displayAvdList() {
791         try {
792             AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog);
793 
794             mSdkLog.printf("Available Android Virtual Devices:\n");
795 
796             AvdInfo[] avds = avdManager.getValidAvds();
797             for (int index = 0 ; index < avds.length ; index++) {
798                 AvdInfo info = avds[index];
799                 if (index > 0) {
800                     mSdkLog.printf("---------\n");
801                 }
802                 mSdkLog.printf("    Name: %s\n", info.getName());
803                 mSdkLog.printf("    Path: %s\n", info.getPath());
804 
805                 // get the target of the AVD
806                 IAndroidTarget target = info.getTarget();
807                 if (target.isPlatform()) {
808                     mSdkLog.printf("  Target: %s (API level %s)\n", target.getName(),
809                             target.getVersion().getApiString());
810                 } else {
811                     mSdkLog.printf("  Target: %s (%s)\n", target.getName(), target
812                             .getVendor());
813                     mSdkLog.printf("          Based on Android %s (API level %s)\n",
814                             target.getVersionName(), target.getVersion().getApiString());
815                 }
816 
817                 // display some extra values.
818                 Map<String, String> properties = info.getProperties();
819                 if (properties != null) {
820                     String skin = properties.get(AvdManager.AVD_INI_SKIN_NAME);
821                     if (skin != null) {
822                         mSdkLog.printf("    Skin: %s\n", skin);
823                     }
824                     String sdcard = properties.get(AvdManager.AVD_INI_SDCARD_SIZE);
825                     if (sdcard == null) {
826                         sdcard = properties.get(AvdManager.AVD_INI_SDCARD_PATH);
827                     }
828                     if (sdcard != null) {
829                         mSdkLog.printf("  Sdcard: %s\n", sdcard);
830                     }
831                 }
832             }
833 
834             // Are there some unused AVDs?
835             AvdInfo[] badAvds = avdManager.getBrokenAvds();
836 
837             if (badAvds.length == 0) {
838                 return;
839             }
840 
841             mSdkLog.printf("\nThe following Android Virtual Devices could not be loaded:\n");
842             boolean needSeparator = false;
843             for (AvdInfo info : badAvds) {
844                 if (needSeparator) {
845                     mSdkLog.printf("---------\n");
846                 }
847                 mSdkLog.printf("    Name: %s\n", info.getName() == null ? "--" : info.getName());
848                 mSdkLog.printf("    Path: %s\n", info.getPath() == null ? "--" : info.getPath());
849 
850                 String error = info.getErrorMessage();
851                 mSdkLog.printf("   Error: %s\n", error == null ? "Uknown error" : error);
852                 needSeparator = true;
853             }
854         } catch (AndroidLocationException e) {
855             errorAndExit(e.getMessage());
856         }
857     }
858 
859     /**
860      * Creates a new AVD. This is a text based creation with command line prompt.
861      */
createAvd()862     private void createAvd() {
863         // find a matching target
864         int targetId = resolveTargetName(mSdkCommandLine.getParamTargetId());
865         IAndroidTarget[] targets = mSdkManager.getTargets();
866 
867         if (targetId == INVALID_TARGET_ID || targetId > targets.length) {
868             errorAndExit("Target id is not valid. Use '%s list targets' to get the target ids.",
869                     SdkConstants.androidCmdName());
870         }
871 
872         IAndroidTarget target = targets[targetId-1]; // target id is 1-based
873 
874         try {
875             boolean removePrevious = mSdkCommandLine.getFlagForce();
876             AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog);
877 
878             String avdName = mSdkCommandLine.getParamName();
879 
880             if (!AvdManager.RE_AVD_NAME.matcher(avdName).matches()) {
881                 errorAndExit(
882                     "AVD name '%1$s' contains invalid characters.\nAllowed characters are: %2$s",
883                     avdName, AvdManager.CHARS_AVD_NAME);
884                 return;
885             }
886 
887             AvdInfo info = avdManager.getAvd(avdName, false /*validAvdOnly*/);
888             if (info != null) {
889                 if (removePrevious) {
890                     mSdkLog.warning(
891                             "Android Virtual Device '%s' already exists and will be replaced.",
892                             avdName);
893                 } else {
894                     errorAndExit("Android Virtual Device '%s' already exists.\n" +
895                                  "Use --force if you want to replace it.",
896                                  avdName);
897                     return;
898                 }
899             }
900 
901             String paramFolderPath = mSdkCommandLine.getParamLocationPath();
902             File avdFolder = null;
903             if (paramFolderPath != null) {
904                 avdFolder = new File(paramFolderPath);
905             } else {
906                 avdFolder = new File(AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD,
907                         avdName + AvdManager.AVD_FOLDER_EXTENSION);
908             }
909 
910             // Validate skin is either default (empty) or NNNxMMM or a valid skin name.
911             Map<String, String> skinHardwareConfig = null;
912             String skin = mSdkCommandLine.getParamSkin();
913             if (skin != null && skin.length() == 0) {
914                 skin = null;
915             }
916 
917             if (skin != null && target != null) {
918                 boolean valid = false;
919                 // Is it a know skin name for this target?
920                 for (String s : target.getSkins()) {
921                     if (skin.equalsIgnoreCase(s)) {
922                         skin = s;  // Make skin names case-insensitive.
923                         valid = true;
924 
925                         // get the hardware properties for this skin
926                         File skinFolder = avdManager.getSkinPath(skin, target);
927                         File skinHardwareFile = new File(skinFolder, AvdManager.HARDWARE_INI);
928                         if (skinHardwareFile.isFile()) {
929                             skinHardwareConfig = ProjectProperties.parsePropertyFile(
930                                     skinHardwareFile, mSdkLog);
931                         }
932                         break;
933                     }
934                 }
935 
936                 // Is it NNNxMMM?
937                 if (!valid) {
938                     valid = AvdManager.NUMERIC_SKIN_SIZE.matcher(skin).matches();
939                 }
940 
941                 if (!valid) {
942                     displaySkinList(target, "Valid skins: ");
943                     errorAndExit("'%s' is not a valid skin name or size (NNNxMMM)", skin);
944                     return;
945                 }
946             }
947 
948             Map<String, String> hardwareConfig = null;
949             if (target != null && target.isPlatform()) {
950                 try {
951                     hardwareConfig = promptForHardware(target, skinHardwareConfig);
952                 } catch (IOException e) {
953                     errorAndExit(e.getMessage());
954                 }
955             }
956 
957             @SuppressWarnings("unused") // oldAvdInfo is never read, yet useful for debugging
958             AvdInfo oldAvdInfo = null;
959             if (removePrevious) {
960                 oldAvdInfo = avdManager.getAvd(avdName, false /*validAvdOnly*/);
961             }
962 
963             @SuppressWarnings("unused") // newAvdInfo is never read, yet useful for debugging
964             AvdInfo newAvdInfo = avdManager.createAvd(avdFolder,
965                     avdName,
966                     target,
967                     skin,
968                     mSdkCommandLine.getParamSdCard(),
969                     hardwareConfig,
970                     removePrevious,
971                     mSdkLog);
972 
973         } catch (AndroidLocationException e) {
974             errorAndExit(e.getMessage());
975         }
976     }
977 
978     /**
979      * Delete an AVD. If the AVD name is not part of the available ones look for an
980      * invalid AVD (one not loaded due to some error) to remove it too.
981      */
deleteAvd()982     private void deleteAvd() {
983         try {
984             String avdName = mSdkCommandLine.getParamName();
985             AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog);
986             AvdInfo info = avdManager.getAvd(avdName, false /*validAvdOnly*/);
987 
988             if (info == null) {
989                 errorAndExit("There is no Android Virtual Device named '%s'.", avdName);
990                 return;
991             }
992 
993             avdManager.deleteAvd(info, mSdkLog);
994         } catch (AndroidLocationException e) {
995             errorAndExit(e.getMessage());
996         }
997     }
998 
999     /**
1000      * Moves an AVD.
1001      */
moveAvd()1002     private void moveAvd() {
1003         try {
1004             String avdName = mSdkCommandLine.getParamName();
1005             AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog);
1006             AvdInfo info = avdManager.getAvd(avdName, true /*validAvdOnly*/);
1007 
1008             if (info == null) {
1009                 errorAndExit("There is no valid Android Virtual Device named '%s'.", avdName);
1010                 return;
1011             }
1012 
1013             // This is a rename if there's a new name for the AVD
1014             String newName = mSdkCommandLine.getParamMoveNewName();
1015             if (newName != null && newName.equals(info.getName())) {
1016                 // same name, not actually a rename operation
1017                 newName = null;
1018             }
1019 
1020             // This is a move (of the data files) if there's a new location path
1021             String paramFolderPath = mSdkCommandLine.getParamLocationPath();
1022             if (paramFolderPath != null) {
1023                 // check if paths are the same. Use File methods to account for OS idiosyncrasies.
1024                 try {
1025                     File f1 = new File(paramFolderPath).getCanonicalFile();
1026                     File f2 = new File(info.getPath()).getCanonicalFile();
1027                     if (f1.equals(f2)) {
1028                         // same canonical path, so not actually a move
1029                         paramFolderPath = null;
1030                     }
1031                 } catch (IOException e) {
1032                     // Fail to resolve canonical path. Fail now since a move operation might fail
1033                     // later and be harder to recover from.
1034                     errorAndExit(e.getMessage());
1035                     return;
1036                 }
1037             }
1038 
1039             if (newName == null && paramFolderPath == null) {
1040                 mSdkLog.warning("Move operation aborted: same AVD name, same canonical data path");
1041                 return;
1042             }
1043 
1044             // If a rename was requested and no data move was requested, check if the original
1045             // data path is our default constructed from the AVD name. In this case we still want
1046             // to rename that folder too.
1047             if (newName != null && paramFolderPath == null) {
1048                 // Compute the original data path
1049                 File originalFolder = new File(
1050                         AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD,
1051                         info.getName() + AvdManager.AVD_FOLDER_EXTENSION);
1052                 if (originalFolder.equals(info.getPath())) {
1053                     try {
1054                         // The AVD is using the default data folder path based on the AVD name.
1055                         // That folder needs to be adjusted to use the new name.
1056                         File f = new File(AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD,
1057                                      newName + AvdManager.AVD_FOLDER_EXTENSION);
1058                         paramFolderPath = f.getCanonicalPath();
1059                     } catch (IOException e) {
1060                         // Fail to resolve canonical path. Fail now rather than later.
1061                         errorAndExit(e.getMessage());
1062                     }
1063                 }
1064             }
1065 
1066             // Check for conflicts
1067             if (newName != null) {
1068                 if (avdManager.getAvd(newName, false /*validAvdOnly*/) != null) {
1069                     errorAndExit("There is already an AVD named '%s'.", newName);
1070                     return;
1071                 }
1072 
1073                 File ini = info.getIniFile();
1074                 if (ini.equals(AvdInfo.getIniFile(newName))) {
1075                     errorAndExit("The AVD file '%s' is in the way.", ini.getCanonicalPath());
1076                     return;
1077                 }
1078             }
1079 
1080             if (paramFolderPath != null && new File(paramFolderPath).exists()) {
1081                 errorAndExit(
1082                         "There is already a file or directory at '%s'.\nUse --path to specify a different data folder.",
1083                         paramFolderPath);
1084             }
1085 
1086             avdManager.moveAvd(info, newName, paramFolderPath, mSdkLog);
1087         } catch (AndroidLocationException e) {
1088             errorAndExit(e.getMessage());
1089         } catch (IOException e) {
1090             errorAndExit(e.getMessage());
1091         }
1092     }
1093 
1094     /**
1095      * Updates a broken AVD.
1096      */
updateAvd()1097     private void updateAvd() {
1098         try {
1099             String avdName = mSdkCommandLine.getParamName();
1100             AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog);
1101             avdManager.updateAvd(avdName, mSdkLog);
1102         } catch (AndroidLocationException e) {
1103             errorAndExit(e.getMessage());
1104         } catch (IOException e) {
1105             errorAndExit(e.getMessage());
1106         }
1107     }
1108 
1109     /**
1110      * Updates adb with the USB devices declared in the SDK add-ons.
1111      */
updateAdb()1112     private void updateAdb() {
1113         try {
1114             mSdkManager.updateAdb();
1115 
1116             mSdkLog.printf(
1117                     "adb has been updated. You must restart adb with the following commands\n" +
1118                     "\tadb kill-server\n" +
1119                     "\tadb start-server\n");
1120         } catch (AndroidLocationException e) {
1121             errorAndExit(e.getMessage());
1122         } catch (IOException e) {
1123             errorAndExit(e.getMessage());
1124         }
1125     }
1126 
1127     /**
1128      * Prompts the user to setup a hardware config for a Platform-based AVD.
1129      * @throws IOException
1130      */
promptForHardware(IAndroidTarget createTarget, Map<String, String> skinHardwareConfig)1131     private Map<String, String> promptForHardware(IAndroidTarget createTarget,
1132             Map<String, String> skinHardwareConfig) throws IOException {
1133         byte[] readLineBuffer = new byte[256];
1134         String result;
1135         String defaultAnswer = "no";
1136 
1137         mSdkLog.printf("%s is a basic Android platform.\n", createTarget.getName());
1138         mSdkLog.printf("Do you wish to create a custom hardware profile [%s]",
1139                 defaultAnswer);
1140 
1141         result = readLine(readLineBuffer).trim();
1142         // handle default:
1143         if (result.length() == 0) {
1144             result = defaultAnswer;
1145         }
1146 
1147         if (getBooleanReply(result) == false) {
1148             // no custom config, return the skin hardware config in case there is one.
1149             return skinHardwareConfig;
1150         }
1151 
1152         mSdkLog.printf("\n"); // empty line
1153 
1154         // get the list of possible hardware properties
1155         File hardwareDefs = new File (mOsSdkFolder + File.separator +
1156                 SdkConstants.OS_SDK_TOOLS_LIB_FOLDER, SdkConstants.FN_HARDWARE_INI);
1157         Map<String, HardwareProperty> hwMap = HardwareProperties.parseHardwareDefinitions(
1158                 hardwareDefs, null /*sdkLog*/);
1159 
1160         HashMap<String, String> map = new HashMap<String, String>();
1161 
1162         // we just want to loop on the HardwareProperties
1163         HardwareProperty[] hwProperties = hwMap.values().toArray(
1164                 new HardwareProperty[hwMap.size()]);
1165         for (int i = 0 ; i < hwProperties.length ;) {
1166             HardwareProperty property = hwProperties[i];
1167 
1168             String description = property.getDescription();
1169             if (description != null) {
1170                 mSdkLog.printf("%s: %s\n", property.getAbstract(), description);
1171             } else {
1172                 mSdkLog.printf("%s\n", property.getAbstract());
1173             }
1174 
1175             String defaultValue = property.getDefault();
1176             String defaultFromSkin = skinHardwareConfig != null ? skinHardwareConfig.get(
1177                     property.getName()) : null;
1178 
1179             if (defaultFromSkin != null) {
1180                 mSdkLog.printf("%s [%s (from skin)]:", property.getName(), defaultFromSkin);
1181             } else if (defaultValue != null) {
1182                 mSdkLog.printf("%s [%s]:", property.getName(), defaultValue);
1183             } else {
1184                 mSdkLog.printf("%s (%s):", property.getName(), property.getType());
1185             }
1186 
1187             result = readLine(readLineBuffer);
1188             if (result.length() == 0) {
1189                 if (defaultFromSkin != null || defaultValue != null) {
1190                     if (defaultFromSkin != null) {
1191                         // we need to write this one in the AVD file
1192                         map.put(property.getName(), defaultFromSkin);
1193                     }
1194 
1195                     mSdkLog.printf("\n"); // empty line
1196                     i++; // go to the next property if we have a valid default value.
1197                          // if there's no default, we'll redo this property
1198                 }
1199                 continue;
1200             }
1201 
1202             switch (property.getType()) {
1203                 case BOOLEAN:
1204                     try {
1205                         if (getBooleanReply(result)) {
1206                             map.put(property.getName(), "yes");
1207                             i++; // valid reply, move to next property
1208                         } else {
1209                             map.put(property.getName(), "no");
1210                             i++; // valid reply, move to next property
1211                         }
1212                     } catch (IOException e) {
1213                         // display error, and do not increment i to redo this property
1214                         mSdkLog.printf("\n%s\n", e.getMessage());
1215                     }
1216                     break;
1217                 case INTEGER:
1218                     try {
1219                         Integer.parseInt(result);
1220                         map.put(property.getName(), result);
1221                         i++; // valid reply, move to next property
1222                     } catch (NumberFormatException e) {
1223                         // display error, and do not increment i to redo this property
1224                         mSdkLog.printf("\n%s\n", e.getMessage());
1225                     }
1226                     break;
1227                 case DISKSIZE:
1228                     // TODO check validity
1229                     map.put(property.getName(), result);
1230                     i++; // valid reply, move to next property
1231                     break;
1232             }
1233 
1234             mSdkLog.printf("\n"); // empty line
1235         }
1236 
1237         return map;
1238     }
1239 
1240     /**
1241      * Reads the line from the input stream.
1242      * @param buffer
1243      * @throws IOException
1244      */
readLine(byte[] buffer)1245     private String readLine(byte[] buffer) throws IOException {
1246         int count = System.in.read(buffer);
1247 
1248         // is the input longer than the buffer?
1249         if (count == buffer.length && buffer[count-1] != 10) {
1250             // create a new temp buffer
1251             byte[] tempBuffer = new byte[256];
1252 
1253             // and read the rest
1254             String secondHalf = readLine(tempBuffer);
1255 
1256             // return a concat of both
1257             return new String(buffer, 0, count) + secondHalf;
1258         }
1259 
1260         // ignore end whitespace
1261         while (count > 0 && (buffer[count-1] == '\r' || buffer[count-1] == '\n')) {
1262             count--;
1263         }
1264 
1265         return new String(buffer, 0, count);
1266     }
1267 
1268     /**
1269      * Returns the boolean value represented by the string.
1270      * @throws IOException If the value is not a boolean string.
1271      */
getBooleanReply(String reply)1272     private boolean getBooleanReply(String reply) throws IOException {
1273 
1274         for (String valid : BOOLEAN_YES_REPLIES) {
1275             if (valid.equalsIgnoreCase(reply)) {
1276                 return true;
1277             }
1278         }
1279 
1280         for (String valid : BOOLEAN_NO_REPLIES) {
1281             if (valid.equalsIgnoreCase(reply)) {
1282                 return false;
1283             }
1284         }
1285 
1286         throw new IOException(String.format("%s is not a valid reply", reply));
1287     }
1288 
errorAndExit(String format, Object...args)1289     private void errorAndExit(String format, Object...args) {
1290         mSdkLog.error(null, format, args);
1291         System.exit(1);
1292     }
1293 
1294     /**
1295      * Converts a symbolic target name (such as those accepted by --target on the command-line)
1296      * to an internal target index id. A valid target name is either a numeric target id (> 0)
1297      * or a target hash string.
1298      * <p/>
1299      * If the given target can't be mapped, {@link #INVALID_TARGET_ID} (0) is returned.
1300      * It's up to the caller to output an error.
1301      * <p/>
1302      * On success, returns a value > 0.
1303      */
resolveTargetName(String targetName)1304     private int resolveTargetName(String targetName) {
1305 
1306         if (targetName == null) {
1307             return INVALID_TARGET_ID;
1308         }
1309 
1310         targetName = targetName.trim();
1311 
1312         // Case of an integer number
1313         if (targetName.matches("[0-9]*")) {
1314             try {
1315                 int n = Integer.parseInt(targetName);
1316                 return n < 1 ? INVALID_TARGET_ID : n;
1317             } catch (NumberFormatException e) {
1318                 // Ignore. Should not happen.
1319             }
1320         }
1321 
1322         // Let's try to find a platform or addon name.
1323         IAndroidTarget[] targets = mSdkManager.getTargets();
1324         for (int i = 0; i < targets.length; i++) {
1325             if (targetName.equals(targets[i].hashString())) {
1326                 return i + 1;
1327             }
1328         }
1329 
1330         return INVALID_TARGET_ID;
1331     }
1332 }
1333