blob: 8e2479df363378c4078ee4c804ef44ae2d8ac379 [file] [log] [blame]
/*
* Copyright (C) 2011 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 vogar.android;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import vogar.Action;
import vogar.Classpath;
import vogar.Result;
import vogar.Run;
import vogar.TestProperties;
import vogar.Toolchain;
import vogar.commands.Command;
import vogar.tasks.Task;
public final class InstallApkTask extends Task {
public static final String ACTIVITY_CLASS = "vogar.target.TestActivity";
private final Action action;
private final File jar;
private final Run run;
public InstallApkTask(Run run, Action action, File jar) {
super("aapt and push " + action.getName());
this.action = action;
this.jar = jar;
this.run = run;
}
@Override protected Result execute() throws Exception {
// We can't put multiple dex files in one apk.
// We can't just give dex multiple jars with conflicting class names
// With that in mind, the APK packaging strategy is as follows:
// 1. dx to create a dex
// 2. aapt the dex to create apk
// 3. sign the apk
// 4. install the apk
File dex = createDex(action, jar);
File apk = createApk(action, dex);
signApk(apk);
installApk(action, apk);
return Result.SUCCESS;
}
/**
* Returns a single dexfile containing {@code action}'s classes and all
* dependencies.
*/
private File createDex(Action action, File actionJar) {
File dex = run.localFile(action, "classes.dex");
Classpath classesToDex = Classpath.of(actionJar);
classesToDex.addAll(run.classpath);
File localTempDir = run.localDir(action.getName());
// Do not specify additional compile-time-only dependencies,
// because everything gets bundled into classes.dex.
Classpath dependentCp = new Classpath();
run.androidSdk.dex(run.multidex, dex, localTempDir, classesToDex, dependentCp, run.toolchain.getDexer());
return dex;
}
private File createApk (Action action, File dex) {
String androidManifest =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
" package=\"" + packageName(action) + "\">\n" +
" <uses-permission android:name=\"android.permission.INTERNET\" />\n" +
" <application" +
((run.debugging) ? " android:debuggable=\"true\"" : "") + ">\n" +
" <activity android:name=\"" + ACTIVITY_CLASS + "\">\n" +
" <intent-filter>\n" +
" <action android:name=\"android.intent.action.MAIN\" />\n" +
" <category android:name=\"android.intent.category.LAUNCHER\" />\n" +
" </intent-filter>\n" +
" </activity>\n" +
" </application>\n" +
"</manifest>\n";
File androidManifestFile = run.localFile(action, "classes", "AndroidManifest.xml");
try {
FileOutputStream androidManifestOut =
new FileOutputStream(androidManifestFile);
androidManifestOut.write(androidManifest.getBytes("UTF-8"));
androidManifestOut.close();
} catch (IOException e) {
throw new RuntimeException("Problem writing " + androidManifestFile, e);
}
File apk = run.localFile(action, action + ".apk");
run.androidSdk.packageApk(apk, androidManifestFile);
run.androidSdk.addToApk(apk, dex);
run.androidSdk.addToApk(apk, run.localFile(action, "classes", TestProperties.FILE));
return apk;
}
/**
* According to android.content.pm.PackageParser, package name
* "must have at least one '.' separator" Since the qualified name
* may not contain a dot, we prefix containing one to ensure we
* are compliant.
*/
public static String packageName(Action action) {
return "vogar.test." + action.getName();
}
private void signApk(File apkUnsigned) {
/*
* key generated with this command, using "password" for the key and keystore passwords:
* keytool -genkey -v -keystore src/vogar/vogar.keystore \
* -keyalg RSA -validity 10000 -alias vogar
*/
new Command(run.log, "jarsigner",
"--storepass", "password",
"-keystore", run.keystore.getPath(),
apkUnsigned.getPath(),
"vogar")
.execute();
}
private void installApk(Action action, File apkSigned) {
// install the local apk ona the device
run.androidSdk.uninstall(packageName(action));
run.androidSdk.install(apkSigned);
}
}