|
13 | 13 | import android.content.Context;
|
14 | 14 | import android.content.ContextWrapper;
|
15 | 15 | import android.content.Intent;
|
| 16 | +import android.content.pm.PackageInfo; |
| 17 | +import android.content.pm.PackageManager; |
| 18 | +import android.content.pm.Signature; |
16 | 19 | import android.content.res.Resources;
|
17 | 20 | import android.os.Handler;
|
18 | 21 | import android.text.TextUtils;
|
| 22 | +import android.util.Log; |
19 | 23 |
|
20 | 24 | import com.plugin.content.PluginDescriptor;
|
21 | 25 | import com.plugin.content.PluginIntentFilter;
|
|
26 | 30 | import com.plugin.util.LogUtil;
|
27 | 31 | import com.plugin.util.ManifestParser;
|
28 | 32 | import com.plugin.util.FileUtil;
|
| 33 | +import com.plugin.util.PackageVerifyer; |
29 | 34 | import com.plugin.util.RefInvoker;
|
30 | 35 |
|
31 | 36 | import dalvik.system.DexClassLoader;
|
32 | 37 |
|
33 | 38 | public class PluginLoader {
|
| 39 | + |
| 40 | + private static final boolean needVefifyCert = true; |
34 | 41 | private static Application sApplication;
|
35 | 42 | private static Object activityThread;
|
36 | 43 | private static PluginManager pluginManager;
|
@@ -141,48 +148,76 @@ public static boolean isInstalled(String pluginId, String pluginVersion) {
|
141 | 148 | * @return
|
142 | 149 | */
|
143 | 150 | public static synchronized boolean installPlugin(String srcPluginFile) {
|
144 |
| - LogUtil.d("Install plugin ", srcPluginFile); |
| 151 | + LogUtil.d("开始安装插件", srcPluginFile); |
| 152 | + |
| 153 | + //第0步,先将apk复制到宿主程序私有目录,防止在安装过程中文件被篡改 |
| 154 | + //TODO XXX |
| 155 | + |
| 156 | + // 第1步,验证插件APK签名,如果被篡改过,将获取不到证书 |
| 157 | + //sApplication.getPackageManager().getPackageArchiveInfo(srcPluginFile, PackageManager.GET_SIGNATURES); |
| 158 | + Signature[] pluginSignatures = PackageVerifyer.collectCertificates(srcPluginFile, false); |
| 159 | + if (pluginSignatures == null) { |
| 160 | + LogUtil.e("插件签名验证失败", srcPluginFile); |
| 161 | + return false; |
| 162 | + } else if (needVefifyCert) { |
| 163 | + //可选步骤,验证插件APK证书是否和宿主程序证书相同。 |
| 164 | + //证书中存放的是公钥和算法信息,而公钥和私钥是1对1的 |
| 165 | + //公钥相同意味着是同一个作者发布的程序 |
| 166 | + Signature[] mainSignatures = null; |
| 167 | + try { |
| 168 | + PackageInfo pkgInfo = sApplication.getPackageManager().getPackageInfo(sApplication.getPackageName(), PackageManager.GET_SIGNATURES); |
| 169 | + mainSignatures = pkgInfo.signatures; |
| 170 | + } catch (PackageManager.NameNotFoundException e) { |
| 171 | + e.printStackTrace(); |
| 172 | + } |
| 173 | + if (!PackageVerifyer.isSignaturesSame(mainSignatures, pluginSignatures)) { |
| 174 | + LogUtil.d("插件证书和宿主证书不一致", srcPluginFile); |
| 175 | + return false; |
| 176 | + } |
| 177 | + } |
145 | 178 |
|
146 |
| - boolean isInstallSuccess = false; |
147 |
| - // 第一步,读取插件描述文件 |
| 179 | + // 第2步,解析Manifest,获得插件详情 |
148 | 180 | PluginDescriptor pluginDescriptor = ManifestParser.parseManifest(srcPluginFile);
|
149 | 181 | if (pluginDescriptor == null || TextUtils.isEmpty(pluginDescriptor.getPackageName())) {
|
150 |
| - return isInstallSuccess; |
| 182 | + LogUtil.d("解析插件Manifest文件失败", srcPluginFile); |
| 183 | + return false; |
151 | 184 | }
|
152 | 185 |
|
153 |
| - // 第二步,检查插件是否已经存在,若存在删除旧的 |
| 186 | + // 第2步,检查插件是否已经存在,若存在删除旧的 |
154 | 187 | PluginDescriptor oldPluginDescriptor = getPluginDescriptorByPluginId(pluginDescriptor.getPackageName());
|
155 | 188 | if (oldPluginDescriptor != null) {
|
156 | 189 | remove(pluginDescriptor.getPackageName());
|
157 | 190 | }
|
158 | 191 |
|
159 |
| - // 第三步骤,复制插件到插件目录 |
160 |
| - if (pluginDescriptor != null) { |
161 |
| - |
162 |
| - String destPluginFile = genInstallPath(pluginDescriptor.getPackageName(), pluginDescriptor.getVersion()); |
163 |
| - boolean isCopySuccess = FileUtil.copyFile(srcPluginFile, destPluginFile); |
164 |
| - if (isCopySuccess) { |
165 |
| - |
166 |
| - //第四步,复制插件so到插件so目录, 在构造插件Dexclassloader的时候,会使用这个so目录作为参数 |
167 |
| - File tempDir = new File(new File(destPluginFile).getParentFile(), "temp"); |
168 |
| - Set<String> soList = FileUtil.unZipSo(srcPluginFile, tempDir); |
169 |
| - if (soList != null) { |
170 |
| - for (String soName : soList) { |
171 |
| - FileUtil.copySo(tempDir, soName, new File(destPluginFile).getParent() + File.separator + "lib"); |
172 |
| - } |
| 192 | + // 第4步骤,复制插件到插件目录 |
| 193 | + String destPluginFile = genInstallPath(pluginDescriptor.getPackageName(), pluginDescriptor.getVersion()); |
| 194 | + boolean isCopySuccess = FileUtil.copyFile(srcPluginFile, destPluginFile); |
| 195 | + if (isCopySuccess) { |
| 196 | + |
| 197 | + //第5步,复制插件so到插件so目录, 在构造插件Dexclassloader的时候,会使用这个so目录作为参数 |
| 198 | + File tempDir = new File(new File(destPluginFile).getParentFile(), "temp"); |
| 199 | + Set<String> soList = FileUtil.unZipSo(srcPluginFile, tempDir); |
| 200 | + if (soList != null) { |
| 201 | + for (String soName : soList) { |
| 202 | + FileUtil.copySo(tempDir, soName, new File(destPluginFile).getParent() + File.separator + "lib"); |
173 | 203 | }
|
| 204 | + } |
174 | 205 |
|
175 |
| - // 第五步 添加到已安装插件列表 |
176 |
| - pluginDescriptor.setInstalledPath(destPluginFile); |
177 |
| - isInstallSuccess = pluginManager.addOrReplace(pluginDescriptor); |
| 206 | + // 第6步 添加到已安装插件列表 |
| 207 | + pluginDescriptor.setInstalledPath(destPluginFile); |
| 208 | + boolean isInstallSuccess = pluginManager.addOrReplace(pluginDescriptor); |
178 | 209 |
|
179 |
| - if (isInstallSuccess) { |
180 |
| - changeListener.onPluginInstalled(pluginDescriptor.getPackageName(), pluginDescriptor.getVersion()); |
181 |
| - } |
| 210 | + if (isInstallSuccess) { |
| 211 | + changeListener.onPluginInstalled(pluginDescriptor.getPackageName(), pluginDescriptor.getVersion()); |
| 212 | + LogUtil.d("安装插件成功", srcPluginFile); |
| 213 | + return true; |
182 | 214 | }
|
| 215 | + } else { |
| 216 | + LogUtil.d("复制插件到安装目录失败", srcPluginFile); |
183 | 217 | }
|
184 | 218 |
|
185 |
| - return isInstallSuccess; |
| 219 | + LogUtil.d("安装插件失败", srcPluginFile); |
| 220 | + return false; |
186 | 221 | }
|
187 | 222 |
|
188 | 223 | /**
|
|
0 commit comments