• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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 vogar.android;
18 
19 import java.io.File;
20 import java.io.FileOutputStream;
21 import java.io.IOException;
22 import vogar.Action;
23 import vogar.Classpath;
24 import vogar.Result;
25 import vogar.Run;
26 import vogar.TestProperties;
27 import vogar.commands.Command;
28 import vogar.tasks.Task;
29 
30 public final class InstallApkTask extends Task {
31     public static final String ACTIVITY_CLASS = "vogar.target.TestActivity";
32 
33     private final Action action;
34     private final File jar;
35     private final Run run;
36 
InstallApkTask(Run run, Action action, File jar)37     public InstallApkTask(Run run, Action action, File jar) {
38         super("aapt and push " + action.getName());
39         this.action = action;
40         this.jar = jar;
41         this.run = run;
42     }
43 
execute()44     @Override protected Result execute() throws Exception {
45         // We can't put multiple dex files in one apk.
46         // We can't just give dex multiple jars with conflicting class names
47 
48         // With that in mind, the APK packaging strategy is as follows:
49         // 1. dx to create a dex
50         // 2. aapt the dex to create apk
51         // 3. sign the apk
52         // 4. install the apk
53         File dex = createDex(action, jar);
54         File apk = createApk(action, dex);
55         signApk(apk);
56         installApk(action, apk);
57         return Result.SUCCESS;
58     }
59 
60     /**
61      * Returns a single dexfile containing {@code action}'s classes and all
62      * dependencies.
63      */
createDex(Action action, File actionJar)64     private File createDex(Action action, File actionJar) {
65         File dex = run.localFile(action, "classes.dex");
66         Classpath classesToDex = Classpath.of(actionJar);
67         classesToDex.addAll(run.classpath);
68         if (run.useJack) {
69             // TODO Implement Jack support for mode=activity.
70             throw new UnsupportedOperationException(
71                     "Jack support for --mode=activity not yet implemented");
72         }
73 
74         File localTempDir = run.localDir(action.getName());
75 
76         // Do not specify additional compile-time-only dependencies,
77         // because everything gets bundled into classes.dex.
78         Classpath dependentCp = new Classpath();
79 
80         run.androidSdk.dex(run.multidex, dex, localTempDir, classesToDex, dependentCp);
81         return dex;
82     }
83 
createApk(Action action, File dex)84     private File createApk (Action action, File dex) {
85         String androidManifest =
86             "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
87             "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
88             "      package=\"" + packageName(action) + "\">\n" +
89             "    <uses-permission android:name=\"android.permission.INTERNET\" />\n" +
90             "    <application" +
91                     ((run.debugging) ? " android:debuggable=\"true\"" : "") + ">\n" +
92             "        <activity android:name=\"" + ACTIVITY_CLASS + "\">\n" +
93             "            <intent-filter>\n" +
94             "                <action android:name=\"android.intent.action.MAIN\" />\n" +
95             "                <category android:name=\"android.intent.category.LAUNCHER\" />\n" +
96             "            </intent-filter>\n" +
97             "        </activity>\n" +
98             "    </application>\n" +
99             "</manifest>\n";
100         File androidManifestFile = run.localFile(action, "classes", "AndroidManifest.xml");
101         try {
102             FileOutputStream androidManifestOut =
103                     new FileOutputStream(androidManifestFile);
104             androidManifestOut.write(androidManifest.getBytes("UTF-8"));
105             androidManifestOut.close();
106         } catch (IOException e) {
107             throw new RuntimeException("Problem writing " + androidManifestFile, e);
108         }
109 
110         File apk = run.localFile(action, action + ".apk");
111         run.androidSdk.packageApk(apk, androidManifestFile);
112         run.androidSdk.addToApk(apk, dex);
113         run.androidSdk.addToApk(apk, run.localFile(action, "classes", TestProperties.FILE));
114         return apk;
115     }
116 
117     /**
118      * According to android.content.pm.PackageParser, package name
119      * "must have at least one '.' separator" Since the qualified name
120      * may not contain a dot, we prefix containing one to ensure we
121      * are compliant.
122      */
packageName(Action action)123     public static String packageName(Action action) {
124         return "vogar.test." + action.getName();
125     }
126 
signApk(File apkUnsigned)127     private void signApk(File apkUnsigned) {
128         /*
129          * key generated with this command, using "password" for the key and keystore passwords:
130          *     keytool -genkey -v -keystore src/vogar/vogar.keystore \
131          *         -keyalg RSA -validity 10000 -alias vogar
132          */
133         new Command(run.log, "jarsigner",
134                 "--storepass", "password",
135                 "-keystore", run.keystore.getPath(),
136                 apkUnsigned.getPath(),
137                 "vogar")
138                 .execute();
139     }
140 
installApk(Action action, File apkSigned)141     private void installApk(Action action, File apkSigned) {
142         // install the local apk ona the device
143         run.androidSdk.uninstall(packageName(action));
144         run.androidSdk.install(apkSigned);
145     }
146 }
147