1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package com.simpligility.maven.plugins.android;
17  
18  import com.android.SdkConstants;
19  import com.android.annotations.Nullable;
20  import com.android.repository.Revision;
21  import com.android.sdklib.AndroidTargetHash;
22  import com.android.sdklib.AndroidVersion;
23  import com.android.sdklib.BuildToolInfo;
24  import com.android.sdklib.IAndroidTarget;
25  import com.android.sdklib.repository.AndroidSdkHandler;
26  import com.android.sdklib.repository.targets.AndroidTargetManager;
27  import org.apache.maven.plugin.MojoExecutionException;
28  
29  import java.io.File;
30  import java.io.FileInputStream;
31  import java.io.IOException;
32  import java.util.Properties;
33  
34  
35  
36  
37  
38  
39  
40  public class AndroidSdk
41  {
42      
43  
44  
45  
46      private static final String DEFAULT_ANDROID_API_LEVEL = "23";
47      
48  
49  
50      private static final String SOURCE_PROPERTIES_FILENAME = "source.properties";
51      
52  
53  
54      private static final String SDK_TOOLS_REVISION_PROPERTY = "Pkg.Revision";
55  
56      
57  
58  
59      private static final String PLATFORMS_FOLDER_NAME = "platforms";
60  
61      private static final String BIN_FOLDER_NAME_IN_TOOLS = "bin";
62  
63      private static final String PARAMETER_MESSAGE = "Please provide a proper Android SDK directory path as "
64              + "configuration parameter <sdk><path>...</path></sdk> in the plugin <configuration/>. As an alternative,"
65              + " you may add the parameter to commandline: -Dandroid.sdk.path=... or set environment variable "
66              + AbstractAndroidMojo.ENV_ANDROID_HOME + ".";
67  
68      private final File sdkPath;
69      private File platformToolsPath;
70      private File toolsPath;
71  
72      private final IAndroidTarget androidTarget;
73      private AndroidSdkHandler sdkManager;
74      private int sdkMajorVersion;
75      private String buildToolsVersion;
76      private ProgressIndicatorImpl progressIndicator;
77  
78      public AndroidSdk( File sdkPath, String apiLevel )
79      {
80          this( sdkPath, apiLevel, null );
81      }
82  
83      public AndroidSdk( File sdkPath, String apiLevel, @Nullable String buildToolsVersion )
84      {
85          this.sdkPath = sdkPath;
86          this.buildToolsVersion = buildToolsVersion;
87          this.progressIndicator = new ProgressIndicatorImpl();
88  
89          if ( sdkPath != null )
90          {
91              sdkManager = AndroidSdkHandler.getInstance( sdkPath );
92              platformToolsPath = new File( sdkPath, SdkConstants.FD_PLATFORM_TOOLS );
93              toolsPath = new File( sdkPath, SdkConstants.FD_TOOLS );
94  
95              if ( sdkManager == null )
96              {
97                  throw invalidSdkException( sdkPath, apiLevel );
98              }
99          }
100         loadSDKToolsMajorVersion();
101 
102         if ( apiLevel == null )
103         {
104             apiLevel = DEFAULT_ANDROID_API_LEVEL;
105         }
106 
107         androidTarget = findPlatformByApiLevel( apiLevel );
108         if ( androidTarget == null )
109         {
110             throw invalidSdkException( sdkPath, apiLevel );
111         }
112     }
113 
114     private InvalidSdkException invalidSdkException( File sdkPath, String platformOrApiLevel )
115     {
116         throw new InvalidSdkException( "Invalid SDK: Platform/API level " + platformOrApiLevel
117                 + " not available. This command should give you all you need:\n" + sdkPath.getAbsolutePath()
118                 + File.separator + "tools" + File.separator + "android update sdk --no-ui --obsolete --force" );
119     }
120 
121     private IAndroidTarget findPlatformByApiLevel( String apiLevel )
122     {
123         
124         AndroidVersion version = null;
125         try
126         {
127             version = new AndroidVersion( apiLevel );
128             String hashString = AndroidTargetHash.getPlatformHashString( version );
129             IAndroidTarget target = sdkManager.getAndroidTargetManager( progressIndicator )
130                     .getTargetFromHashString( hashString, progressIndicator );
131 
132             
133             
134             if ( target != null && target.getLocation() != null )
135             {
136                 return target;
137             }
138         }
139         catch ( AndroidVersion.AndroidVersionException ignore )
140         {
141             throw new InvalidSdkException( "Error AndroidVersion: " + ignore.getMessage() );
142         }
143 
144         
145         for ( IAndroidTarget t: sdkManager.getAndroidTargetManager( null ).getTargets( null ) )
146         {
147             if ( t.isPlatform() && apiLevel.equals( t.getVersionName() ) )
148             {
149                 return t;
150             }
151         }
152         return null;
153     }
154 
155     private void assertPathIsDirectory( final File path )
156     {
157         if ( path == null )
158         {
159             throw new InvalidSdkException( PARAMETER_MESSAGE );
160         }
161         if ( !path.isDirectory() )
162         {
163             throw new InvalidSdkException( "Path \"" + path + "\" is not a directory. " + PARAMETER_MESSAGE );
164         }
165     }
166 
167     
168 
169 
170 
171 
172     public String getAaptPath()
173     {
174         return getPathForBuildTool( BuildToolInfo.PathId.AAPT );
175     }
176 
177     
178 
179 
180 
181     public String getAidlPath()
182     {
183         return getPathForBuildTool( BuildToolInfo.PathId.AIDL );
184     }
185 
186     
187 
188 
189 
190     public String getDxJarPath()
191     {
192         return getPathForBuildTool( BuildToolInfo.PathId.DX_JAR );
193     }
194 
195     
196 
197 
198     public String getD8JarPath()
199     {
200         final File pathToDexJar = new File( getPathForBuildTool( BuildToolInfo.PathId.DX_JAR ) );
201         final File pathToD8Jar = new File( pathToDexJar.getParent(), "d8.jar" );
202         return pathToD8Jar.getAbsolutePath();
203     }
204 
205     
206 
207 
208 
209     public String getProguardJarPath()
210     {
211         File directory = new File( getToolsPath(), "proguard" + File.separator + "lib" + File.separator );
212         File proguardJar = new File( directory, "proguard.jar" );
213         if ( proguardJar.exists() ) 
214         {
215             return proguardJar.getAbsolutePath();
216         }
217         throw new InvalidSdkException( "Cannot find " + proguardJar );
218     }
219     
220     
221 
222 
223 
224     public String getShrinkedAndroidJarPath()
225     {
226         File shrinkedAndroidJar = new File( getBuildToolsLibDirectoryPath(), "shrinkedAndroid.jar" );
227         if ( shrinkedAndroidJar.exists() ) 
228         {
229             return shrinkedAndroidJar.getAbsolutePath();
230         }
231         throw new InvalidSdkException( "Cannot find " + shrinkedAndroidJar );
232     }
233     
234     
235 
236 
237 
238     public String getBuildToolsLibDirectoryPath()
239     {
240         File buildToolsLib = new File( getBuildToolInfo().getLocation(), "lib" );
241         if ( buildToolsLib.exists() ) 
242         {
243             return buildToolsLib.getAbsolutePath();
244         }
245         throw new InvalidSdkException( "Cannot find " + buildToolsLib );
246     }
247     
248     
249 
250 
251 
252     public String getMainDexClassesRulesPath()
253     {
254         File mainDexClassesRules = new File( getBuildToolInfo().getLocation(),
255                 "mainDexClasses.rules" );
256         if ( mainDexClassesRules.exists() ) 
257         {
258             return mainDexClassesRules.getAbsolutePath();
259         }
260         throw new InvalidSdkException( "Cannot find " + mainDexClassesRules );
261     }
262 
263     public void assertThatBuildToolsVersionIsAtLeast( String version, String feature ) 
264             throws InvalidSdkException, NumberFormatException 
265     {
266         if ( getBuildToolInfo().getRevision().
267                 compareTo( Revision.parseRevision( version ) ) < 0 )
268         {
269             throw new InvalidSdkException( "Version of build tools must be at least " 
270                     + version + " for " + feature + " to work" );
271         }
272     }
273     
274     
275 
276 
277 
278 
279     public String getAdbPath()
280     {
281         return getPathForPlatformTool( SdkConstants.FN_ADB );
282     }
283 
284     
285 
286 
287 
288 
289     public String getZipalignPath()
290     {
291         return getPathForBuildTool( BuildToolInfo.PathId.ZIP_ALIGN );
292     }
293 
294     
295 
296 
297 
298 
299     public String getLintPath()
300     {
301         return getPathForTool( BIN_FOLDER_NAME_IN_TOOLS + "/" + "lint" + ext( ".bat", "" ) );
302     }
303 
304     
305 
306 
307 
308 
309     public String getMonkeyRunnerPath()
310     {
311         return getPathForTool( BIN_FOLDER_NAME_IN_TOOLS + "/" + "monkeyrunner" + ext( ".bat", "" ) );
312     }
313 
314     
315 
316 
317 
318 
319     public String getApkBuilderPath()
320     {
321         return getPathForTool( "apkbuilder" + ext( ".bat", "" ) );
322     }
323 
324     
325 
326 
327 
328 
329     public String getAndroidPath()
330     {
331         String cmd = "android";
332         String ext = SdkConstants.currentPlatform() == 2 ? ".bat" : "";
333 
334         return getPathForTool( cmd + ext );
335     }
336 
337     
338 
339 
340 
341     public File getToolsPath()
342     {
343         return toolsPath;
344     }
345 
346     private String getPathForBuildTool( BuildToolInfo.PathId pathId )
347     {
348         return getBuildToolInfo().getPath( pathId );
349     }
350     
351     private BuildToolInfo getBuildToolInfo()
352     {
353         
354         if ( buildToolsVersion != null && !buildToolsVersion.equals( "" ) )
355         {
356             BuildToolInfo buildToolInfo = sdkManager.getBuildToolInfo( Revision.parseRevision( buildToolsVersion ),
357                     progressIndicator );
358             if ( buildToolInfo != null )
359             {
360                 return buildToolInfo;
361             }
362             
363             
364             throw new InvalidSdkException( "Invalid SDK: Build-tools " + buildToolsVersion + " not found."
365                     + " Check your Android SDK to install the build tools " + buildToolsVersion );
366         }
367 
368         if ( androidTarget != null )
369         {
370             BuildToolInfo buildToolInfo = androidTarget.getBuildToolInfo();
371             if ( buildToolInfo != null ) 
372             {
373                 return buildToolInfo;
374             }
375         }
376         
377         BuildToolInfo latestBuildToolInfo = sdkManager.getLatestBuildTool( progressIndicator, true );
378         if ( latestBuildToolInfo == null )
379         {
380             throw new InvalidSdkException( "Invalid SDK: Build-tools not found. Check the content of '" 
381                 + sdkPath.getAbsolutePath() + File.separator + "build-tools', or run '" 
382                 + sdkPath.getAbsolutePath() + File.separator + "tools" + File.separator 
383                 + "android sdk' to install them" );
384         }
385         return latestBuildToolInfo;
386     }
387 
388     private String getPathForPlatformTool( String tool )
389     {
390         return new File( platformToolsPath, tool ).getAbsolutePath();
391     }
392 
393     private String getPathForTool( String tool )
394     {
395         return new File( toolsPath, tool ).getAbsolutePath();
396     }
397 
398     private static String ext( String windowsExtension, String nonWindowsExtension )
399     {
400         if ( SdkConstants.currentPlatform() == SdkConstants.PLATFORM_WINDOWS )
401         {
402             return windowsExtension;
403         }
404         else
405         {
406             return nonWindowsExtension;
407         }
408     }
409 
410     
411 
412 
413 
414 
415     public String getPathForFrameworkAidl()
416     {
417         return androidTarget.getPath( IAndroidTarget.ANDROID_AIDL );
418     }
419 
420     
421 
422 
423 
424 
425 
426 
427     public File getAndroidJar() throws MojoExecutionException
428     {
429         final String androidJarPath = androidTarget.getPath( IAndroidTarget.ANDROID_JAR );
430         if ( androidJarPath == null )
431         {
432             throw new MojoExecutionException( "No AndroidJar found for " + androidTarget.getLocation() );
433         }
434         return new File ( androidJarPath );
435     }
436   
437     
438 
439 
440 
441 
442 
443 
444     public File getSdkPath() throws MojoExecutionException
445     {
446         if ( sdkPath.exists() )
447         {
448             return sdkPath;
449         }
450         throw new MojoExecutionException( "Can't find the SDK directory : " + sdkPath.getAbsolutePath() );
451     }
452 
453     
454 
455 
456 
457     public File getPlatform()
458     {
459         assertPathIsDirectory( sdkPath );
460 
461         final File platformsDirectory = new File( sdkPath, PLATFORMS_FOLDER_NAME );
462         assertPathIsDirectory( platformsDirectory );
463 
464         final File platformDirectory;
465         if ( androidTarget == null )
466         {
467             IAndroidTarget latestTarget = null;
468             AndroidTargetManager targetManager = sdkManager.getAndroidTargetManager( progressIndicator );
469             for ( IAndroidTarget target: targetManager.getTargets( progressIndicator ) )
470             {
471                 if ( target.isPlatform() )
472                 {
473                     if ( latestTarget == null
474                             || target.getVersion().getApiLevel() > latestTarget.getVersion().getApiLevel() )
475                     {
476                         latestTarget = target;
477                     }
478                 }
479             }
480             platformDirectory = new File ( latestTarget.getLocation() );
481         }
482         else
483         {
484             platformDirectory = new File( androidTarget.getLocation() );
485         }
486         assertPathIsDirectory( platformDirectory );
487         return platformDirectory;
488     }
489 
490     
491 
492 
493     private void loadSDKToolsMajorVersion()
494     {
495         File propFile = new File( sdkPath, "tools/" + SOURCE_PROPERTIES_FILENAME );
496         Properties properties = new Properties();
497         try
498         {
499             properties.load( new FileInputStream( propFile ) );
500         }
501         catch ( IOException e )
502         {
503             throw new InvalidSdkException( "Error reading " + propFile.getAbsoluteFile() );
504         }
505 
506         if ( properties.containsKey( SDK_TOOLS_REVISION_PROPERTY ) )
507         {
508             try
509             {
510                 String versionString = properties.getProperty( SDK_TOOLS_REVISION_PROPERTY );
511                 String majorVersion;
512                 if ( versionString.matches( ".*[\\.| ].*" ) )
513                 {
514                     String[] versions = versionString.split( "[\\.| ]" );
515                     majorVersion = versions[ 0 ];
516                 }
517                 else
518                 {
519                     majorVersion = versionString;
520                 }
521                 sdkMajorVersion = Integer.parseInt( majorVersion );
522             }
523             catch ( NumberFormatException e )
524             {
525                 throw new InvalidSdkException( "Error - The property '" + SDK_TOOLS_REVISION_PROPERTY
526                         + "' in the SDK source.properties file  number is not an Integer: "
527                         + properties.getProperty( SDK_TOOLS_REVISION_PROPERTY ) );
528             }
529         }
530     }
531 
532     
533 
534 
535 
536 
537     public int getSdkMajorVersion()
538     {
539         return sdkMajorVersion;
540     }
541 }