Add support for building AILD files.
Xavier Ducrohet [Wed, 5 Sep 2012 01:37:00 +0000 (18:37 -0700)]
This brings in the SourceProcessor, SourceGenerator and
DependencyGraph from the ant tasks. The dependency graph
is only needed because Gradle doesn't give us the list
of changed files just yet.

Change-Id: I4e9c8ccb032078529516af29b2287b21adac05cc

25 files changed:
builder/src/main/java/com/android/builder/AndroidBuilder.java
builder/src/main/java/com/android/builder/AndroidDependency.java
builder/src/main/java/com/android/builder/CommandLineRunner.java
builder/src/main/java/com/android/builder/VariantConfiguration.java
builder/src/main/java/com/android/builder/compiler/AidlProcessor.java [new file with mode: 0644]
builder/src/main/java/com/android/builder/compiler/DependencyGraph.java [new file with mode: 0644]
builder/src/main/java/com/android/builder/compiler/InputPath.java [new file with mode: 0644]
builder/src/main/java/com/android/builder/compiler/SourceGenerator.java [new file with mode: 0644]
builder/src/test/java/com/android/builder/MockSourceSet.java
builder/src/test/java/com/android/builder/VariantConfigurationTest.java
gradle/src/main/groovy/com/android/build/gradle/AndroidBasePlugin.groovy
gradle/src/main/groovy/com/android/build/gradle/AndroidLibraryPlugin.groovy
gradle/src/main/groovy/com/android/build/gradle/AndroidPlugin.groovy
gradle/src/main/groovy/com/android/build/gradle/CompileAidlTask.groovy [new file with mode: 0644]
testapps/aidl/build.gradle [new file with mode: 0644]
testapps/aidl/src/main/AndroidManifest.xml [new file with mode: 0644]
testapps/aidl/src/main/aidl/com/android/tests/basicprojectwithaidl/ITest.aidl [new file with mode: 0644]
testapps/aidl/src/main/aidl/com/android/tests/basicprojectwithaidl/Rect.aidl [new file with mode: 0644]
testapps/aidl/src/main/java/com/android/tests/basicprojectwithaidl/Main.java [new file with mode: 0644]
testapps/aidl/src/main/java/com/android/tests/basicprojectwithaidl/Rect.java [new file with mode: 0644]
testapps/aidl/src/main/res/drawable/icon.png [new file with mode: 0644]
testapps/aidl/src/main/res/layout/main.xml [new file with mode: 0644]
testapps/aidl/src/main/res/values/strings.xml [new file with mode: 0644]
testapps/applibtest/lib/src/main/aidl/com/android/tests/basicprojectwithaidl/ITest.aidl [new file with mode: 0644]
testapps/applibtest/lib/src/main/aidl/com/android/tests/basicprojectwithaidl/Rect.aidl [new file with mode: 0644]

index 5489f84..9be56f1 100644 (file)
@@ -19,6 +19,8 @@ package com.android.builder;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.annotations.VisibleForTesting;
+import com.android.builder.compiler.AidlProcessor;
+import com.android.builder.compiler.SourceGenerator;
 import com.android.builder.packaging.DuplicateFileException;
 import com.android.builder.packaging.JavaResourceProcessor;
 import com.android.builder.packaging.Packager;
@@ -57,7 +59,7 @@ import java.util.Set;
  * {@link #generateBuildConfig(String, java.util.List)}
  * {@link #processManifest(String)}
  * {@link #processResources(String, String, String, String, String, AaptOptions)}
- * {@link #convertBytecode(java.util.List, String, DexOptions)}
+ * {@link #convertBytecode(java.util.List, java.util.List, String, DexOptions)}
  * {@link #packageApk(String, String, String, String)}
  *
  * Java compilation is not handled but the builder provides the runtime classpath with
@@ -404,6 +406,24 @@ public class AndroidBuilder {
         }
     }
 
+    /**
+     *
+     * Process the resources and generate R.java and/or the packaged resources.
+     *
+     * Call this directly if you don't care about checking whether the inputs have changed.
+     * Otherwise, get the input first to check with {@link VariantConfiguration#getResourceInputs()}
+     * and then call (or not),
+     * {@link #processResources(String, String, java.util.List, String, String, String, AaptOptions)}.
+
+     * @param manifestFile the location of the manifest file
+     * @param preprocessResDir the pre-processed folder
+     * @param sourceOutputDir optional source folder to generate R.java
+     * @param resPackageOutput optional filepath for packaged resources
+     * @param proguardOutput optional filepath for proguard file to generate
+     * @param options the {@link AaptOptions}
+     * @throws IOException
+     * @throws InterruptedException
+     */
     public void processResources(
             @NonNull String manifestFile,
             @Nullable String preprocessResDir,
@@ -416,7 +436,21 @@ public class AndroidBuilder {
                 resPackageOutput, proguardOutput, options);
     }
 
-        public void processResources(
+    /**
+     * Process the resources and generate R.java and/or the packaged resources.
+     *
+     *
+     * @param manifestFile the location of the manifest file
+     * @param preprocessResDir the pre-processed folder
+     * @param resInputs the res folder inputs
+     * @param sourceOutputDir optional source folder to generate R.java
+     * @param resPackageOutput optional filepath for packaged resources
+     * @param proguardOutput optional filepath for proguard file to generate
+     * @param options the {@link AaptOptions}
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    public void processResources(
             @NonNull String manifestFile,
             @Nullable String preprocessResDir,
             @NonNull List<File> resInputs,
@@ -602,6 +636,44 @@ public class AndroidBuilder {
         mCmdLineRunner.runCmdLine(command);
     }
 
+    /**
+     * compiles all AIDL files.
+     *
+     * Call this directly if you don't care about checking whether the imports have changed.
+     * Otherwise, get the imports first to check with
+     * {@link com.android.builder.VariantConfiguration#getAidlImports()}
+     * and then call (or not), {@link #compileAidl(java.util.List, java.io.File, java.util.List)}.
+     *
+     * @param sourceFolders
+     * @param sourceOutputDir
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    public void compileAidl(@NonNull List<File> sourceFolders,
+                            @NonNull File sourceOutputDir)
+            throws IOException, InterruptedException {
+        compileAidl(sourceFolders, sourceOutputDir, mVariant.getAidlImports());
+    }
+
+    public void compileAidl(@NonNull List<File> sourceFolders,
+                            @NonNull File sourceOutputDir,
+                            @NonNull List<File> importFolders)
+            throws IOException, InterruptedException {
+
+        SourceGenerator compiler = new SourceGenerator(mLogger);
+
+        @SuppressWarnings("deprecation")
+        String aidlPath = mTarget.getPath(IAndroidTarget.AIDL);
+
+        AidlProcessor processor = new AidlProcessor(
+                aidlPath,
+                mTarget.getPath(IAndroidTarget.ANDROID_AIDL),
+                importFolders,
+                mCmdLineRunner);
+
+        compiler.processFiles(processor, sourceFolders, sourceOutputDir);
+    }
+
     public void convertBytecode(
             @NonNull List<String> classesLocation,
             @NonNull List<String> libraries,
index b667fa6..9bbb84a 100644 (file)
@@ -56,6 +56,11 @@ public interface AndroidDependency {
     File getJniFolder();
 
     /**
+     * Returns the location of the aidl import folder.
+     */
+    File getAidlFolder();
+
+    /**
      * Returns the location of the proguard files.
      */
     File getProguardRules();
index 99e55ad..b3cdd76 100644 (file)
@@ -25,7 +25,7 @@ import com.android.utils.ILogger;
 import java.io.IOException;
 import java.util.List;
 
-class CommandLineRunner {
+public class CommandLineRunner {
 
     private final ILogger mLogger;
 
@@ -51,7 +51,6 @@ class CommandLineRunner {
     /**
      * Get the stderr output of a process and return when the process is done.
      * @param process The process to get the output from
-     * @param stderr The array to store the stderr output
      * @return the process return code.
      * @throws InterruptedException
      */
index bd5f96c..b151c4f 100644 (file)
@@ -260,11 +260,11 @@ public class VariantConfiguration {
         return !mDirectLibraryProjects.isEmpty();
     }
 
-    public Iterable<AndroidDependency> getDirectLibraries() {
+    public List<AndroidDependency> getDirectLibraries() {
         return mDirectLibraryProjects;
     }
 
-    public Iterable<AndroidDependency> getFlatLibraries() {
+    public List<AndroidDependency> getFlatLibraries() {
         return mFlatLibraryProjects;
     }
 
@@ -439,6 +439,25 @@ public class VariantConfiguration {
     }
 
     /**
+     * Returns all the aidl import folder that are outside of the current project.
+     *
+     * @return
+     */
+    public List<File> getAidlImports() {
+        List<File> list = new ArrayList<File>();
+
+        for (AndroidDependency lib : mFlatLibraryProjects) {
+            File aidlLib = lib.getAidlFolder();
+            if (aidlLib != null && aidlLib.isDirectory()) {
+                list.add(aidlLib);
+            }
+        }
+
+        return list;
+    }
+
+
+    /**
      * Returns the compile classpath for this config. If the config tests a library, this
      * will include the classpath of the tested config
      * @return
diff --git a/builder/src/main/java/com/android/builder/compiler/AidlProcessor.java b/builder/src/main/java/com/android/builder/compiler/AidlProcessor.java
new file mode 100644 (file)
index 0000000..f5d633a
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.builder.compiler;
+
+import com.android.annotations.NonNull;
+import com.android.builder.CommandLineRunner;
+import com.android.builder.compiler.SourceGenerator.DisplayType;
+import com.android.utils.ILogger;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ */
+public class AidlProcessor implements SourceGenerator.Processor {
+
+    private final String mAidlExecutable;
+    private final String mFrameworkLocation;
+    private final List<File> mImportFolders;
+    private final CommandLineRunner mRunner;
+
+    public AidlProcessor(@NonNull String aidlExecutable,
+                         @NonNull String frameworkLocation,
+                         @NonNull List<File> importFolders,
+                         @NonNull CommandLineRunner runner) {
+        mAidlExecutable = aidlExecutable;
+        mFrameworkLocation = frameworkLocation;
+        mImportFolders = importFolders;
+        mRunner = runner;
+    }
+
+    @Override
+    public String getSourceFileExtension() {
+        return "aidl";
+    }
+
+    @Override
+    public void process(File filePath, List<File> sourceFolders, File sourceOutputDir,
+                        ILogger logger)
+            throws IOException, InterruptedException {
+
+        ArrayList<String> command = new ArrayList<String>();
+
+        command.add(mAidlExecutable);
+
+        command.add("-p" + mFrameworkLocation);
+        command.add("-o" + sourceOutputDir.getAbsolutePath());
+        // add all the source folders as import in case an aidl file in a source folder
+        // imports a parcelable from another source folder.
+        for (File sourceFolder : sourceFolders) {
+            if (sourceFolder.isDirectory()) {
+                command.add("-I" + sourceFolder.getAbsolutePath());
+            }
+        }
+
+        // add all the library aidl folders to access parcelables that are in libraries
+        for (File f : mImportFolders) {
+            command.add("-I" + f.getAbsolutePath());
+        }
+
+        // set auto dependency file creation
+        command.add("-a");
+
+        command.add(filePath.getAbsolutePath());
+
+        logger.info("aidl command: %s", command.toString());
+
+        mRunner.runCmdLine(command);
+    }
+
+    @Override
+    public void displayMessage(ILogger logger, DisplayType type, int count) {
+        switch (type) {
+            case FOUND:
+                logger.info("Found %1$d AIDL files.", count);
+                break;
+            case COMPILING:
+                if (count > 0) {
+                    logger.info("Compiling %1$d AIDL files.", count);
+                } else {
+                    logger.info("No AIDL files to compile.");
+                }
+                break;
+            case REMOVE_OUTPUT:
+                logger.info("Found %1$d obsolete output files to remove.", count);
+                break;
+            case REMOVE_DEP:
+                logger.info("Found %1$d obsolete dependency files to remove.", count);
+                break;
+        }
+    }
+}
diff --git a/builder/src/main/java/com/android/builder/compiler/DependencyGraph.java b/builder/src/main/java/com/android/builder/compiler/DependencyGraph.java
new file mode 100644 (file)
index 0000000..1ae67da
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.builder.compiler;
+
+import com.android.utils.ILogger;
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ *  This class takes care of dependency tracking for all targets and prerequisites listed in
+ *  a single dependency file. A dependency graph always has a dependency file associated with it
+ *  for the duration of its lifetime
+ */
+public class DependencyGraph {
+
+    private final static boolean DEBUG = false;
+    private final ILogger mLogger;
+
+    private static enum DependencyStatus {
+        NONE, NEW_FILE, UPDATED_FILE, MISSING_FILE, ERROR;
+    }
+
+    // Files that we know about from the dependency file
+    private Set<File> mTargets = Collections.emptySet();
+    private Set<File> mPrereqs = mTargets;
+    private File mFirstPrereq = null;
+    private boolean mMissingDepFile = false;
+    private long mDepFileLastModified;
+    private final List<InputPath> mNewInputs;
+
+    public DependencyGraph(File dependencyFilePath, List<InputPath> newInputPaths, ILogger logger) {
+        mNewInputs = newInputPaths;
+        parseDependencyFile(dependencyFilePath);
+        mLogger = logger;
+    }
+
+    /**
+     * Check all the dependencies to see if anything has changed.
+     *
+     * @param printStatus will print to {@link System#out} the dependencies status.
+     * @return true if new prerequisites have appeared, target files are missing or if
+     *         prerequisite files have been modified since the last target generation.
+     */
+    public boolean dependenciesHaveChanged(boolean printStatus) {
+        // If no dependency file has been set up, then we'll just return true
+        // if we have a dependency file, we'll check to see what's been changed
+        if (mMissingDepFile) {
+            mLogger.info("No Dependency File Found");
+            return true;
+        }
+
+        // check for missing output first
+        if (missingTargetFile()) {
+            if (printStatus) {
+                mLogger.info("Found Deleted Target File");
+            }
+            return true;
+        }
+
+        // get the time stamp of the oldest target.
+        long oldestTarget = getOutputLastModified();
+
+        // first look through the input folders and look for new files or modified files.
+        DependencyStatus status = checkInputs(oldestTarget);
+
+        // this can't find missing files. This is done later.
+        switch (status) {
+            case ERROR:
+                throw new RuntimeException();
+            case NEW_FILE:
+                if (printStatus) {
+                    mLogger.info("Found new input file");
+                }
+                return true;
+            case UPDATED_FILE:
+                if (printStatus) {
+                    mLogger.info("Found modified input file");
+                }
+                return true;
+        }
+
+        // now do a full check on the remaining files.
+        status = checkPrereqFiles(oldestTarget);
+        // this can't find new input files. This is done above.
+        switch (status) {
+            case ERROR:
+                throw new RuntimeException();
+            case MISSING_FILE:
+                if (printStatus) {
+                    mLogger.info("Found deleted input file");
+                }
+                return true;
+            case UPDATED_FILE:
+                if (printStatus) {
+                    mLogger.info("Found modified input file");
+                }
+                return true;
+        }
+
+        return false;
+    }
+
+    public Set<File> getTargets() {
+        return Collections.unmodifiableSet(mTargets);
+    }
+
+    public File getFirstPrereq() {
+        return mFirstPrereq;
+    }
+
+    /**
+     * Parses the given dependency file and stores the file paths
+     *
+     * @param dependencyFile the dependency file
+     */
+    private void parseDependencyFile(File dependencyFile) {
+        // first check if the dependency file is here.
+        if (dependencyFile.isFile() == false) {
+            mMissingDepFile = true;
+            return;
+        }
+
+        // get the modification time of the dep file as we may need it later
+        mDepFileLastModified = dependencyFile.lastModified();
+
+        // Read in our dependency file
+        List<String> content = readFile(dependencyFile);
+        if (content == null) {
+            mLogger.error(null, "ERROR: Couldn't read " + dependencyFile.getAbsolutePath());
+            return;
+        }
+
+        // The format is something like:
+        // output1 output2 [...]: dep1 dep2 [...]
+        // move it back to a single line first
+        StringBuilder sb = new StringBuilder();
+        for (String line : content) {
+            line = line.trim();
+            if (line.endsWith("\\")) {
+                line = line.substring(0, line.length() - 1);
+            }
+            sb.append(line);
+        }
+
+        // split the left and right part
+        String[] files = sb.toString().split(":");
+
+        // get the target files:
+        String[] targets = files[0].trim().split(" ");
+
+        String[] prereqs = {};
+        // Check to make sure our dependency file is okay
+        if (files.length < 1) {
+            mLogger.warning(
+                    "Warning! Dependency file does not list any prerequisites after ':' ");
+        } else {
+            // and the prerequisite files:
+            prereqs = files[1].trim().split(" ");
+        }
+
+        mTargets = new HashSet<File>(targets.length);
+        for (String path : targets) {
+            if (path.length() > 0) {
+                mTargets.add(new File(path));
+            }
+        }
+
+        mPrereqs = new HashSet<File>(prereqs.length);
+        for (String path : prereqs) {
+            if (path.length() > 0) {
+                if (DEBUG) {
+                    mLogger.info("PREREQ: " + path);
+                }
+                File f = new File(path);
+                if (mFirstPrereq == null) {
+                    mFirstPrereq = f;
+                }
+                mPrereqs.add(f);
+            }
+        }
+    }
+
+    /**
+     * Check all the input files and folders to see if there have been new
+     * files added to them or if any of the existing files have been modified.
+     *
+     * This looks at the input paths, not at the list of known prereq. Therefore this
+     * will not find missing files. It will however remove processed files from the
+     * prereq file list so that we can process those in a 2nd step.
+     *
+     * This should be followed by a call to {@link #checkPrereqFiles(long)} which
+     * will process the remaining files in the prereq list.
+     *
+     * If a change is found, this will return immediately with either
+     * {@link DependencyStatus#NEW_FILE} or {@link DependencyStatus#UPDATED_FILE}.
+     *
+     * @param oldestTarget the timestamp of the oldest output file to compare against.
+     *
+     * @return the status of the file in the watched folders.
+     *
+     */
+    private DependencyStatus checkInputs(long oldestTarget) {
+        if (mNewInputs != null) {
+            for (InputPath input : mNewInputs) {
+                File file = input.getFile();
+                if (file.isDirectory()) {
+                    DependencyStatus status = checkInputFolder(file, input, oldestTarget);
+                    if (status != DependencyStatus.NONE) {
+                        return status;
+                    }
+                } else if (file.isFile()) {
+                    DependencyStatus status = checkInputFile(file, input, oldestTarget);
+                    if (status != DependencyStatus.NONE) {
+                        return status;
+                    }
+                }
+            }
+        }
+
+        // If we make it all the way through our directories we're good.
+        return DependencyStatus.NONE;
+    }
+
+    /**
+     * Check all the files in the tree under root and check to see if the files are
+     * listed under the dependencies, or if they have been modified. Recurses into subdirs.
+     *
+     * @param folder the folder to search through.
+     * @param inputFolder the root level inputFolder
+     * @param oldestTarget the time stamp of the oldest output file to compare against.
+     *
+     * @return the status of the file in the folder.
+     */
+    private DependencyStatus checkInputFolder(File folder, InputPath inputFolder,
+                                              long oldestTarget) {
+        if (inputFolder.ignores(folder)) {
+            return DependencyStatus.NONE;
+        }
+
+        File[] files = folder.listFiles();
+        if (files == null) {
+            mLogger.error(null, "ERROR " + folder.toString() + " is not a dir or can't be read");
+            return DependencyStatus.ERROR;
+        }
+        // Loop through files in this folder
+        for (File file : files) {
+            // If this is a directory, recurse into it
+            if (file.isDirectory()) {
+                DependencyStatus status = checkInputFolder(file, inputFolder, oldestTarget);
+                if (status != DependencyStatus.NONE) {
+                    return status;
+                }
+            } else if (file.isFile()) {
+                DependencyStatus status = checkInputFile(file, inputFolder, oldestTarget);
+                if (status != DependencyStatus.NONE) {
+                    return status;
+                }
+            }
+        }
+        // If we got to here then we didn't find anything interesting
+        return DependencyStatus.NONE;
+    }
+
+    private DependencyStatus checkInputFile(File file, InputPath inputFolder,
+                                            long oldestTarget) {
+        if (inputFolder.ignores(file)) {
+            return DependencyStatus.NONE;
+        }
+
+        // if it's a file, remove it from the list of prereqs.
+        // This way if files in this folder don't trigger a build we'll have less
+        // files to go through manually
+        if (mPrereqs.remove(file) == false) {
+            // turns out this is a new file!
+
+            if (DEBUG) {
+                mLogger.info("NEW FILE: " + file.getAbsolutePath());
+            }
+            return DependencyStatus.NEW_FILE;
+        } else {
+            // check the time stamp on this file if it's a file we care about based what the
+            // input folder decides.
+            if (inputFolder.checksForModification(file)) {
+                if (file.lastModified() > oldestTarget) {
+                    if (DEBUG) {
+                        mLogger.info("UPDATED FILE: " + file.getAbsolutePath());
+                    }
+                    return DependencyStatus.UPDATED_FILE;
+                }
+            }
+        }
+
+        return DependencyStatus.NONE;
+    }
+
+    /**
+     * Check all the prereq files we know about to make sure they're still there, or that they
+     * haven't been modified since the last build.
+     *
+     * @param oldestTarget the time stamp of the oldest output file to compare against.
+     *
+     * @return the status of the files
+     */
+    private DependencyStatus checkPrereqFiles(long oldestTarget) {
+        // TODO: Optimize for the case of a specific file as inputPath.
+        //       We should have a map of filepath to inputpath to quickly search through them?
+
+        // Loop through our prereq files and make sure they still exist
+        for (File prereq : mPrereqs) {
+            if (prereq.exists() == false) {
+                if (DEBUG) {
+                    mLogger.info("MISSING FILE: " + prereq.getAbsolutePath());
+                }
+                return DependencyStatus.MISSING_FILE;
+            }
+
+            // check the time stamp on this file if it's a file we care about.
+            // To know if we care about the file we have to find the matching input.
+            if (mNewInputs != null) {
+                String filePath = prereq.getAbsolutePath();
+                for (InputPath input : mNewInputs) {
+                    File inputFile = input.getFile();
+                    // if the input path is a directory, check if the prereq file is in it,
+                    // otherwise check if the prereq file match exactly the input path.
+                    if (inputFile.isDirectory()) {
+                        if (filePath.startsWith(inputFile.getAbsolutePath())) {
+                            // ok file is inside a directory type input folder.
+                            // check if we need to check this type of file, and if yes, check it.
+                            if (input.checksForModification(prereq)) {
+                                if (prereq.lastModified() > oldestTarget) {
+                                    if (DEBUG) {
+                                        mLogger.info(
+                                                "UPDATED FILE: " + prereq.getAbsolutePath());
+                                    }
+                                    return DependencyStatus.UPDATED_FILE;
+                                }
+                            }
+                        }
+                    } else {
+                        // this is a file input path, we must check if the match is exact.
+                        if (prereq.equals(inputFile)) {
+                            if (input.checksForModification(prereq)) {
+                                if (prereq.lastModified() > oldestTarget) {
+                                    if (DEBUG) {
+                                        mLogger.info(
+                                                "UPDATED FILE: " + prereq.getAbsolutePath());
+                                    }
+                                    return DependencyStatus.UPDATED_FILE;
+                                }
+                            }
+                        }
+                    }
+                }
+            } else {
+                // no input? we consider all files.
+                if (prereq.lastModified() > oldestTarget) {
+                    if (DEBUG) {
+                        mLogger.info("UPDATED FILE: " + prereq.getAbsolutePath());
+                    }
+                    return DependencyStatus.UPDATED_FILE;
+                }
+            }
+        }
+
+        // If we get this far, then all our prereq are okay
+        return DependencyStatus.NONE;
+    }
+
+    /**
+     * Check all the target files we know about to make sure they're still there
+     * @return true if any of the target files are missing.
+     */
+    private boolean missingTargetFile() {
+        // Loop through our target files and make sure they still exist
+        for (File target : mTargets) {
+            if (target.exists() == false) {
+                return true;
+            }
+        }
+        // If we get this far, then all our targets are okay
+        return false;
+    }
+
+    /**
+     * Returns the earliest modification time stamp from all the output targets. If there
+     * are no known target, the dependency file time stamp is returned.
+     */
+    private long getOutputLastModified() {
+        // Find the oldest target
+        long oldestTarget = Long.MAX_VALUE;
+        // if there's no output, then compare to the time of the dependency file.
+        if (mTargets.size() == 0) {
+            oldestTarget = mDepFileLastModified;
+        } else {
+            for (File target : mTargets) {
+                if (target.lastModified() < oldestTarget) {
+                    oldestTarget = target.lastModified();
+                }
+            }
+        }
+
+        return oldestTarget;
+    }
+
+    /**
+     * Reads and returns the content of a text file.
+     * @param file the file path to the text file
+     * @return null if the file could not be read
+     */
+    private static List<String> readFile(File file) {
+        try {
+            return Files.readLines(file, Charsets.UTF_8);
+        } catch (IOException e) {
+            // return null below
+        }
+
+        return null;
+    }
+}
diff --git a/builder/src/main/java/com/android/builder/compiler/InputPath.java b/builder/src/main/java/com/android/builder/compiler/InputPath.java
new file mode 100644 (file)
index 0000000..5f422a5
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.builder.compiler;
+
+import java.io.File;
+import java.util.Set;
+
+public class InputPath {
+
+    private final File mFile;
+    /**
+     * A set of extensions. Only files with an extension in this set will
+     * be considered for a modification check. All deleted/created files will still be
+     * checked.
+     */
+    private final Set<String> mTouchedExtensions;
+
+    public InputPath(File file) {
+        this(file, null);
+    }
+
+    public InputPath(File file, Set<String> extensionsToCheck) {
+        if (file == null) {
+            throw new RuntimeException("File in InputPath(File) can't be null");
+        }
+        mFile = file;
+        mTouchedExtensions = extensionsToCheck;
+    }
+
+    public File getFile() {
+        return mFile;
+    }
+
+    /**
+     * Returns whether this input path (likely actually a folder) must check this files for
+     * modification (all files are checked for add/delete).
+     *
+     * This is configured by constructing the {@link InputPath} with additional restriction
+     * parameters such as specific extensions.
+     * @param file the file to check
+     * @return true if the file must be checked for modification.
+     */
+    public boolean checksForModification(File file) {
+        if (ignores(file)) {
+            return false;
+        }
+
+        if (mTouchedExtensions != null &&
+                mTouchedExtensions.contains(getExtension(file)) == false) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns whether the InputPath ignores a given file or folder. If it is ignored then
+     * the file (or folder) is not checked for any event (modification/add/delete).
+     * If it's a folder, then it and its content are completely ignored.
+     * @param file the file or folder to check
+     * @return true if the file or folder are ignored.
+     */
+    public boolean ignores(File file) {
+        // always ignore hidden files/folders.
+        return file.getName().startsWith(".");
+    }
+
+    /**
+     *  Gets the extension (if present) on a file by looking at the filename
+     *  @param file the file to get the extension from
+     *  @return the extension if present, or the empty string if the filename doesn't have
+     *          and extension.
+     */
+    protected static String getExtension(File file) {
+        return getExtension(file.getName());
+    }
+
+    /**
+     *  Gets the extension (if present) on a file by looking at the filename
+     *  @param fileName the filename to get the extension from
+     *  @return the extension if present, or the empty string if the filename doesn't have
+     *          and extension.
+     */
+    protected static String getExtension(String fileName) {
+        int index = fileName.lastIndexOf('.');
+        if (index == -1) {
+            return "";
+        }
+        // Don't include the leading '.' in the extension
+        return fileName.substring(index + 1);
+    }
+
+}
diff --git a/builder/src/main/java/com/android/builder/compiler/SourceGenerator.java b/builder/src/main/java/com/android/builder/compiler/SourceGenerator.java
new file mode 100644 (file)
index 0000000..c5b8c23
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.builder.compiler;
+
+import com.android.utils.ILogger;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Class to help generating Source code.
+ */
+public class SourceGenerator {
+
+    private final ILogger mLogger;
+
+    static enum DisplayType {
+        FOUND, COMPILING, REMOVE_OUTPUT, REMOVE_DEP;
+    }
+
+    interface Processor {
+        String getSourceFileExtension();
+        void process(File filePath, List<File> sourceFolders, File sourceOutputDir, ILogger logger)
+                throws IOException, InterruptedException;
+        void displayMessage(ILogger logger, DisplayType type, int count);
+    }
+
+    public SourceGenerator(ILogger logger) {
+        mLogger = logger;
+    }
+
+    public void processFiles(Processor processor, List<File> sourceFolders,
+                             File sourceOutputDir) throws IOException, InterruptedException {
+
+        String extension = processor.getSourceFileExtension();
+
+        // gather all the source files from all the source folders.
+        Map<File, File> sourceFiles = getFilesByNameEntryFilter(sourceFolders, extension);
+        if (sourceFiles.size() > 0) {
+            processor.displayMessage(mLogger, DisplayType.FOUND, sourceFiles.size());
+        }
+
+        // go look for all dependency files in the gen folder. This will have all dependency
+        // files but we can filter them based on the first pre-req file.
+        List<File> depFiles = getFilesByNameEntryFilter(sourceOutputDir, "d");
+
+        // parse all the dep files and keep the ones that are of the proper type and check if
+        // they require compilation again.
+        Map<File, File> toCompile = new HashMap<File, File>();
+        ArrayList<File> toRemove = new ArrayList<File>();
+        ArrayList<File> depsToRemove = new ArrayList<File>();
+        for (File depFile : depFiles) {
+            DependencyGraph graph = new DependencyGraph(depFile, null /*watchPaths*/, mLogger);
+
+            // get the source file. it's the first item in the pre-reqs
+            File sourceFile = graph.getFirstPrereq();
+            String sourceFilePath = sourceFile.getAbsolutePath();
+
+            // The gen folder may contain other dependency files not generated by this particular
+            // processor.
+            // We only care if the first pre-rep is of the right extension.
+            if (sourceFilePath.toLowerCase(Locale.US).endsWith("." + extension)) {
+                // remove from the list of sourceFiles to mark as "processed" (but not compiled
+                // yet, that'll be done by adding it to toCompile)
+                File sourceFolder = sourceFiles.get(sourceFile);
+                if (sourceFolder == null) {
+                    // looks like the source file does not exist anymore!
+                    // we'll have to remove the output!
+                    Set<File> outputFiles = graph.getTargets();
+                    toRemove.addAll(outputFiles);
+
+                    // also need to remove the dep file.
+                    depsToRemove.add(depFile);
+                } else {
+                    // Source file is present. remove it from the list as being processed.
+                    sourceFiles.remove(sourceFile);
+
+                    // check if it needs to be recompiled.
+                    if (graph.dependenciesHaveChanged(false /*printStatus*/)) {
+                        toCompile.put(sourceFile, sourceFolder);
+                    }
+                }
+            }
+        }
+
+        // add to the list of files to compile, whatever is left in sourceFiles. Those are
+        // new files that have never been compiled.
+        toCompile.putAll(sourceFiles);
+
+        processor.displayMessage(mLogger, DisplayType.COMPILING, toCompile.size());
+        if (toCompile.size() > 0) {
+            for (Map.Entry<File, File> toCompilePath : toCompile.entrySet()) {
+                processor.process(toCompilePath.getKey(), sourceFolders, sourceOutputDir, mLogger);
+            }
+        }
+
+        if (toRemove.size() > 0) {
+            processor.displayMessage(mLogger, DisplayType.REMOVE_OUTPUT, toRemove.size());
+
+            for (File toRemoveFile : toRemove) {
+                if (toRemoveFile.delete() == false) {
+                    mLogger.warning("Failed to remove " + toRemoveFile.getAbsolutePath());
+                }
+            }
+        }
+
+        // remove the dependency files that are obsolete
+        if (depsToRemove.size() > 0) {
+            processor.displayMessage(mLogger, DisplayType.REMOVE_DEP, toRemove.size());
+
+            for (File file : depsToRemove) {
+                if (file.delete() == false) {
+                    mLogger.warning("Failed to remove " + file.getAbsolutePath());
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns a list of files found in given folders, all matching a given filter.
+     * The result is a map of (file, folder).
+     * @param folders the folders to search
+     * @param extension the filter for the files. Typically a glob.
+     * @return a map of (file, folder)
+     */
+    private Map<File, File> getFilesByNameEntryFilter(List<File> folders, String extension) {
+        Map<File, File> sourceFiles = new HashMap<File, File>();
+
+        for (File folder : folders) {
+            List<File> files = getFilesByNameEntryFilter(folder, extension);
+
+            for (File f : files) {
+                sourceFiles.put(f, folder);
+            }
+        }
+
+        return sourceFiles;
+    }
+
+    /**
+     * Returns a list of files found in a given folder, matching a given filter.
+     * @param sourceFolder the folders to search
+     * @param extension The file extension
+     * @return an iterator.
+     */
+    private List<File> getFilesByNameEntryFilter(File sourceFolder, String extension) {
+        ArrayList<File> result = new ArrayList<File>();
+
+        if (sourceFolder.isDirectory()) {
+            gatherFiles(sourceFolder, extension, result);
+        }
+        return result;
+    }
+
+    private void gatherFiles(File folder, String extension, ArrayList<File> result) {
+        for (File f : folder.listFiles()) {
+            if (f.isFile()) {
+                String name = f.getName();
+                if (name.substring(name.lastIndexOf('.') + 1).equals(extension)) {
+                    result.add(f);
+                }
+            } else if (f.isDirectory()) {
+                gatherFiles(f, extension, result);
+            }
+        }
+    }
+}
index 6a5d15c..2d2c174 100644 (file)
@@ -37,6 +37,11 @@ class MockSourceSet implements SourceSet {
     }
 
     @Override
+    public Iterable<File> getCompileClasspath() {
+        return null;
+    }
+
+    @Override
     public File getAndroidResources() {
         return new File(mRoot, "res");
     }
index 2be0a48..c1ee5a5 100644 (file)
@@ -110,7 +110,7 @@ public class VariantConfigurationTest extends TestCase {
                 mBuildType, new MockSourceSet("debug"),
                 VariantConfiguration.Type.DEFAULT) {
             @Override
-            String getPackageFromManifest() {
+            public String getPackageFromManifest() {
                 return packageName;
             }
             // don't do validation.
index ae39942..aa886ef 100644 (file)
@@ -214,7 +214,7 @@ abstract class AndroidBasePlugin {
         processResources.variant = variant
         processResources.configObjects = variant.configObjects
         processResources.conventionMapping.manifestFile = { processManifestTask.processedManifest }
-        // TODO: unify with generateBuilderConfig somehow?
+        // TODO: unify with generateBuilderConfig, and compileAidl somehow?
         processResources.conventionMapping.sourceOutputDir = {
             project.file("$project.buildDir/source/$variant.dirName")
         }
@@ -240,12 +240,43 @@ abstract class AndroidBasePlugin {
         return processResources
     }
 
+    protected CompileAidlTask createAidlTask(ApplicationVariant variant) {
+
+        VariantConfiguration config = variant.config
+
+        def compileTask = project.tasks.add("compile${variant.name}Aidl", CompileAidlTask)
+        compileTask.plugin = this
+        compileTask.variant = variant
+        compileTask.configObjects = variant.configObjects
+
+        List<Object> sourceList = new ArrayList<Object>();
+        sourceList.add(config.defaultSourceSet.aidlSource)
+        if (config.getType() != VariantConfiguration.Type.TEST) {
+            sourceList.add(config.buildTypeSourceSet.aidlSource)
+        }
+        if (config.hasFlavors()) {
+            for (com.android.builder.SourceSet flavorSourceSet : config.flavorSourceSets) {
+                sourceList.add(flavorSourceSet.aidlSource)
+            }
+        }
+
+        compileTask.sourceDirs = sourceList
+        compileTask.importDirs = variant.config.aidlImports
+
+        compileTask.conventionMapping.sourceOutputDir = {
+            project.file("$project.buildDir/source/$variant.dirName")
+        }
+
+        return compileTask
+    }
+
     protected void createCompileTask(ApplicationVariant variant,
                                      ApplicationVariant testedVariant,
                                      ProcessResourcesTask processResources,
-                                     GenerateBuildConfigTask generateBuildConfigTask) {
+                                     GenerateBuildConfigTask generateBuildConfigTask,
+                                     CompileAidlTask aidlTask) {
         def compileTask = project.tasks.add("compile${variant.name}", Compile)
-        compileTask.dependsOn processResources, generateBuildConfigTask
+        compileTask.dependsOn processResources, generateBuildConfigTask, aidlTask
 
         VariantConfiguration config = variant.config
 
@@ -308,8 +339,11 @@ abstract class AndroidBasePlugin {
         // Add a task to generate resource source files
         def processResources = createProcessResTask(variant, processManifestTask, crunchTask)
 
+        def compileAidl = createAidlTask(variant)
+
         // Add a task to compile the test application
-        createCompileTask(variant, testedVariant, processResources, generateBuildConfigTask)
+        createCompileTask(variant, testedVariant, processResources, generateBuildConfigTask,
+                compileAidl)
 
         Task assembleTask = addPackageTasks(variant, null, true /*isTestApk*/)
 
index 6ff815d..c376e62 100644 (file)
@@ -86,8 +86,11 @@ class AndroidLibraryPlugin extends AndroidBasePlugin implements Plugin<Project>
         def processResources = createProcessResTask(variant, processManifestTask,
                 null /*crunchTask*/)
 
+        def compileAidl = createAidlTask(variant)
+
         // Add a compile task
-        createCompileTask(variant, null/*testedVariant*/, processResources, generateBuildConfigTask)
+        createCompileTask(variant, null/*testedVariant*/, processResources, generateBuildConfigTask,
+                compileAidl)
 
         // jar the classes.
         Jar jar = project.tasks.add("${buildTypeData.buildType.name}Jar", Jar);
@@ -100,17 +103,24 @@ class AndroidLibraryPlugin extends AndroidBasePlugin implements Plugin<Project>
         jar.exclude(packageName + "/Manifest.class")
         jar.exclude(packageName + "/Manifest\$*.class")
 
-        // merge the resources into the bundle folder
-        Copy mergeRes = project.tasks.add("merge${variant.name}Res",
-                Copy)
-        // mergeRes from 3 sources. the order is important to make sure the override works well.
+        // package the resources into the bundle folder
+        Copy packageRes = project.tasks.add("package${variant.name}Res", Copy)
+        // packageRes from 3 sources. the order is important to make sure the override works well.
         // TODO: fix the case of values -- need to merge the XML!
-        mergeRes.from(defaultConfigData.androidSourceSet.androidResources,
+        packageRes.from(defaultConfigData.androidSourceSet.androidResources,
                 buildTypeData.androidSourceSet.androidResources)
-        mergeRes.into(project.file("$project.buildDir/$DIR_BUNDLES/${variant.dirName}/res"))
+        packageRes.into(project.file("$project.buildDir/$DIR_BUNDLES/${variant.dirName}/res"))
+
+        // package the aidl files into the bundle folder
+        Copy packageAidl = project.tasks.add("package${variant.name}Aidl", Copy)
+        // packageAidl from 3 sources. the order is important to make sure the override works well.
+        // TODO: fix the case of values -- need to merge the XML!
+        packageAidl.from(defaultConfigData.androidSourceSet.aidlSource,
+                buildTypeData.androidSourceSet.aidlSource)
+        packageAidl.into(project.file("$project.buildDir/$DIR_BUNDLES/${variant.dirName}/aidl"))
 
         Zip bundle = project.tasks.add("bundle${variant.name}", Zip)
-        bundle.dependsOn jar, mergeRes
+        bundle.dependsOn jar, packageRes, packageAidl
         bundle.setDescription("Assembles a bundle containing the library in ${variant.name}.");
         bundle.destinationDir = project.file("$project.buildDir/libs")
         bundle.extension = "alb"
@@ -140,7 +150,7 @@ class AndroidLibraryPlugin extends AndroidBasePlugin implements Plugin<Project>
 
             @Override
             File getResFolder() {
-                return mergeRes.destinationDir
+                return packageRes.destinationDir
             }
 
             @Override
@@ -154,6 +164,11 @@ class AndroidLibraryPlugin extends AndroidBasePlugin implements Plugin<Project>
             }
 
             @Override
+            File getAidlFolder() {
+                return packageAidl.destinationDir
+            }
+
+            @Override
             File getProguardRules() {
                 return null
             }
index 530a732..9fa9c93 100644 (file)
@@ -247,8 +247,11 @@ class AndroidPlugin extends AndroidBasePlugin implements Plugin<Project> {
         // Add a task to generate resource source files
         def processResources = createProcessResTask(variant, processManifestTask, crunchTask)
 
+        def compileAidl = createAidlTask(variant)
+
         // Add a compile task
-        createCompileTask(variant, null/*testedVariant*/, processResources, generateBuildConfigTask)
+        createCompileTask(variant, null/*testedVariant*/, processResources, generateBuildConfigTask,
+                compileAidl)
 
         Task returnTask = addPackageTasks(variant, assembleTask, isTestApk)
         if (returnTask != null) {
diff --git a/gradle/src/main/groovy/com/android/build/gradle/CompileAidlTask.groovy b/gradle/src/main/groovy/com/android/build/gradle/CompileAidlTask.groovy
new file mode 100644 (file)
index 0000000..b1efbfe
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle
+
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.TaskAction
+
+/**
+ */
+class CompileAidlTask extends BaseAndroidTask {
+
+    @InputFiles
+    List<File> sourceDirs
+
+
+    @InputFiles
+    List<File> importDirs
+
+    @OutputDirectory
+    File sourceOutputDir
+
+    @TaskAction
+    void generate() {
+        getBuilder().compileAidl(getSourceDirs(), getSourceOutputDir(), getImportDirs())
+    }
+}
diff --git a/testapps/aidl/build.gradle b/testapps/aidl/build.gradle
new file mode 100644 (file)
index 0000000..be4c4d9
--- /dev/null
@@ -0,0 +1,17 @@
+//
+// A basic Android application that follows all the conventions
+//
+buildscript {
+    repositories {
+        maven { url '../../repo' }
+    }
+    dependencies {
+        classpath 'com.android.build:gradle-android:0.1-SNAPSHOT'
+    }
+}
+apply plugin: 'android'
+
+android {
+    target = "android-15"
+
+}
\ No newline at end of file
diff --git a/testapps/aidl/src/main/AndroidManifest.xml b/testapps/aidl/src/main/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..4cf7553
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      android:versionCode="1"
+      android:versionName="1.0" package="com.android.tests.basicprojectwithaidl">
+    <application android:label="@string/app_name" android:icon="@drawable/icon">
+        <activity android:name="com.android.tests.basicprojectwithaidlwithaidl.Main"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+    <uses-sdk android:minSdkVersion="AOSP"/>
+</manifest>
diff --git a/testapps/aidl/src/main/aidl/com/android/tests/basicprojectwithaidl/ITest.aidl b/testapps/aidl/src/main/aidl/com/android/tests/basicprojectwithaidl/ITest.aidl
new file mode 100644 (file)
index 0000000..9b81031
--- /dev/null
@@ -0,0 +1,7 @@
+package com.android.tests.basicprojectwithaidl;
+
+interface ITest {
+    Rect getRect();
+    int getInt();
+}
+
diff --git a/testapps/aidl/src/main/aidl/com/android/tests/basicprojectwithaidl/Rect.aidl b/testapps/aidl/src/main/aidl/com/android/tests/basicprojectwithaidl/Rect.aidl
new file mode 100644 (file)
index 0000000..734cf77
--- /dev/null
@@ -0,0 +1,5 @@
+package com.android.tests.basicprojectwithaidl;
+
+// Declare Rect so AIDL can find it and knows that it implements
+// the parcelable protocol.
+parcelable Rect;
\ No newline at end of file
diff --git a/testapps/aidl/src/main/java/com/android/tests/basicprojectwithaidl/Main.java b/testapps/aidl/src/main/java/com/android/tests/basicprojectwithaidl/Main.java
new file mode 100644 (file)
index 0000000..eaed510
--- /dev/null
@@ -0,0 +1,15 @@
+package com.android.tests.basicprojectwithaidl;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class Main extends Activity
+{
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+}
diff --git a/testapps/aidl/src/main/java/com/android/tests/basicprojectwithaidl/Rect.java b/testapps/aidl/src/main/java/com/android/tests/basicprojectwithaidl/Rect.java
new file mode 100644 (file)
index 0000000..8e16926
--- /dev/null
@@ -0,0 +1,52 @@
+package com.android.tests.basicprojectwithaidl;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class Rect implements Parcelable {
+    public int left;
+    public int top;
+    public int right;
+    public int bottom;
+
+    public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() {
+        public Rect createFromParcel(Parcel in) {
+            return new Rect(in);
+        }
+
+        public Rect[] newArray(int size) {
+            return new Rect[size];
+        }
+    };
+
+    public Rect() {
+    }
+
+    private Rect(Parcel in) {
+        readFromParcel(in);
+    }
+
+    public void writeToParcel(Parcel out) {
+        out.writeInt(left);
+        out.writeInt(top);
+        out.writeInt(right);
+        out.writeInt(bottom);
+    }
+
+    public void readFromParcel(Parcel in) {
+        left = in.readInt();
+        top = in.readInt();
+        right = in.readInt();
+        bottom = in.readInt();
+    }
+
+    public int describeContents() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public void writeToParcel(Parcel arg0, int arg1) {
+        // TODO Auto-generated method stub
+
+    }
+}
diff --git a/testapps/aidl/src/main/res/drawable/icon.png b/testapps/aidl/src/main/res/drawable/icon.png
new file mode 100644 (file)
index 0000000..a07c69f
Binary files /dev/null and b/testapps/aidl/src/main/res/drawable/icon.png differ
diff --git a/testapps/aidl/src/main/res/layout/main.xml b/testapps/aidl/src/main/res/layout/main.xml
new file mode 100644 (file)
index 0000000..783e4a0
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+<TextView
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:text="Basic Project"
+    />
+</LinearLayout>
+
diff --git a/testapps/aidl/src/main/res/values/strings.xml b/testapps/aidl/src/main/res/values/strings.xml
new file mode 100644 (file)
index 0000000..a7322d3
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">basicProject</string>
+</resources>
diff --git a/testapps/applibtest/lib/src/main/aidl/com/android/tests/basicprojectwithaidl/ITest.aidl b/testapps/applibtest/lib/src/main/aidl/com/android/tests/basicprojectwithaidl/ITest.aidl
new file mode 100644 (file)
index 0000000..9b81031
--- /dev/null
@@ -0,0 +1,7 @@
+package com.android.tests.basicprojectwithaidl;
+
+interface ITest {
+    Rect getRect();
+    int getInt();
+}
+
diff --git a/testapps/applibtest/lib/src/main/aidl/com/android/tests/basicprojectwithaidl/Rect.aidl b/testapps/applibtest/lib/src/main/aidl/com/android/tests/basicprojectwithaidl/Rect.aidl
new file mode 100644 (file)
index 0000000..734cf77
--- /dev/null
@@ -0,0 +1,5 @@
+package com.android.tests.basicprojectwithaidl;
+
+// Declare Rect so AIDL can find it and knows that it implements
+// the parcelable protocol.
+parcelable Rect;
\ No newline at end of file