|
最近为了解一个盒子上app在我们平台上无法工作的问题,去浅尝了反编译apk并在smali中注入代码,推荐一个叫Android Killer的IDE工具,几乎集成了所有反编译和打包的功能。同时也了解该app使用的插件加载,之所以会使用到插件来完成该功能,因为该app涉及到底层的audio实现,audio在framework会在各家平台有不同定制,利用插件就能保证对主程序来说平台都一样,而插件就根据各个平台的不同去做不同实现。
下面简要介绍一下该app的插件实现方法:
1、插件也是以apk的形式存在的
先来看看插件的Manifest
<?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34;?>
<manifest android:versionCode=&#34;1&#34; android:versionName=&#34;1.0&#34; package=&#34;com.kelvinwu.earlycmccplugin&#34; platformBuildVersionCode=&#34;19&#34; platformBuildVersionName=&#34;4.4.2-1456859&#34;
xmlns:android=&#34;http://schemas.android.com/apk/res/android&#34;>
<application>
<meta-data android:name=&#34;IPlugin&#34; android:value=&#34;com.kelvinwu.cmccplugin.EarlyAppEvent&#34; />
<meta-data android:name=&#34;IMicrophone&#34; android:value=&#34;com.kelvinwu.cmccplugin.MicrophoneController&#34; />
<meta-data android:name=&#34;IKaraoke&#34; android:value=&#34;com.kelvinwu.cmccplugin.EarlyISingEvent&#34; />
<meta-data android:name=&#34;IAudioTrack&#34; android:value=&#34;com.kelvinwu.cmccplugin.AudioTracker&#34; />
<meta-data android:name=&#34;IAudioTrackProfile&#34; android:value=&#34;com.kelvinwu.cmccplugin.AudioTrackProfile&#34; />
<meta-data android:name=&#34;HardWareLevel&#34; android:value=&#34;2&#34; />
<meta-data android:name=&#34;Manufacturer&#34; android:value=&#34;kelvinwu&#34; />
<service android:name=&#34;com.kelvinwu.cmccplugin.PluginService&#34;>
<intent-filter>
<action android:name=&#34;com.cmcc.karaoke.plugin&#34; />
</intent-filter>
</service>
</application>
</manifest>
可以看到application下有两类tag:meta-data和service
meta-data:标签中name是接口的名字,value是实现该接口的具体实现类的名字,主程序中可以根据需要实现的接口去拿实现类的对象。
service:定义了一个有很特殊的名字的action,这个的作用是针对已经安装好的插件,主app能够通过pm去查询这样的一个service,用于辨别哪些package是插件。
这里的插件有两种存在方式:1、被安装(通过特殊的service去查询即可) 2、未安装(apk包中asset自带)
app会优先去查找系统中已经安装过的,如果找不到会用apk包中带的插件包。
2、如何使用插件中的类
app中提供了一个插件工具类,主要功能就是根据我们需要的接口类,根据插件创建出具体的实现类。
InterfaceClass I = function(InterfaceClass.class);
对外就这样封装使用很简单方便,下面看看代码实现,因为是反编译的就不是很好看,基本原理很简单。
final class d
implements b
{
private static final String b = MainApplication.b().getApplicationInfo().dataDir;
private static final String c = b + &#34;/plugin/asset.apk&#34;;
private static final String d = b + &#34;/plugin/asset.cfg&#34;;
private static final String e = b + &#34;/plugin/libs&#34;;
private static final String f = b + &#34;/plugin/opt&#34;;
HashMap<String, ClassLoader> a = new HashMap();
private ApplicationInfo g = null;
/*
返回一个可用的插件的application
*/
private ApplicationInfo a()
{
Object localObject2;
StringBuilder localStringBuilder;
if (this.g == null)
{
//优先查找已经安装的package
localObject1 = new ArrayList();
localObject2 = com.iflytek.app.b.b().getPackageManager().queryIntentServices(new Intent(&#34;com.cmcc.karaoke.plugin&#34;), 128).iterator();
while (((Iterator)localObject2).hasNext()) {
((ArrayList)localObject1).add(((ResolveInfo)((Iterator)localObject2).next()).serviceInfo.applicationInfo);
}
//从已经安装的package中找到第一个能满足需求的package的applicationinfo
this.g = a((List)localObject1);
localObject2 = com.iflytek.log.b.a(this);
localStringBuilder = new StringBuilder(&#34;verify installed packages pass = &#34;);
if (this.g == null)
{
localObject1 = &#34;null&#34;;
((com.iflytek.log.b)localObject2).c((String)localObject1);
}
}
else if (this.g == null)
{
//其次查找asset中自带的插件
this.g = b();
localObject2 = com.iflytek.log.b.a(this);
localStringBuilder = new StringBuilder(&#34;verify assets packages pass = &#34;);
if (this.g != null) {
break label233;
}
}
label233:
for (Object localObject1 = &#34;null&#34;;; localObject1 = this.g.packageName)
{
((com.iflytek.log.b)localObject2).c((String)localObject1);
if (this.g == null)
{
this.g = d(&#34;empty.apk&#34;);
com.iflytek.log.b.a(this).c(&#34;use empty package = &#34; + this.g.packageName);
}
return this.g;
localObject1 = this.g.packageName;
break;
}
}
/*
参数1:apk绝对路径
参数2:native lib库路径
*/
private static ApplicationInfo a(String paramString1, String paramString2)
{
PackageInfo localPackageInfo = MainApplication.b().getPackageManager().getPackageArchiveInfo(paramString1, 128);
if (localPackageInfo != null)
{
localPackageInfo.applicationInfo.sourceDir = paramString1;
localPackageInfo.applicationInfo.nativeLibraryDir = paramString2;
return localPackageInfo.applicationInfo;
}
return null;
}
/*
选出符合要求的application
*/
private ApplicationInfo a(List<ApplicationInfo> paramList)
{
paramList = paramList.iterator();
while (paramList.hasNext())
{
ApplicationInfo localApplicationInfo = (ApplicationInfo)paramList.next();
if (a(localApplicationInfo)) {
return localApplicationInfo;
}
}
return null;
}
private Object a(ApplicationInfo paramApplicationInfo, String paramString)
{
try
{
String str1 = paramApplicationInfo.packageName;
String str2 = paramApplicationInfo.sourceDir;
String str3 = paramApplicationInfo.nativeLibraryDir;
ClassLoader localClassLoader = (ClassLoader)this.a.get(str1);
paramApplicationInfo = localClassLoader;
if (localClassLoader == null)
{
paramApplicationInfo = new File(f);
if (!paramApplicationInfo.exists()) {
paramApplicationInfo.mkdirs();
}
paramApplicationInfo = new DexClassLoader(str2, f, str3, MainApplication.b()getClassLoader());
this.a.put(str1, paramApplicationInfo);
}
paramApplicationInfo = Class.forName(paramString, true, paramApplicationInfo).newInstance();
return paramApplicationInfo;
}
catch (Exception paramApplicationInfo)
{
paramApplicationInfo.printStackTrace();
}
return null;
}
private <T> T a(ApplicationInfo paramApplicationInfo, String paramString, Class<T> paramClass)
{
paramApplicationInfo = a(paramApplicationInfo, paramString);
if ((paramApplicationInfo != null) && (paramClass.isInstance(paramApplicationInfo))) {
return paramApplicationInfo;
}
throw new InstantiationError(&#34;pluginClassName:&#34; + paramString + &#34;Instance fail&#34;);
}
/*
判断系统是否满足底层需求,判断的方法存在于插件当中
*/
private boolean a(ApplicationInfo paramApplicationInfo)
{
String str = paramApplicationInfo.metaData.getString(&#34;IPlugin&#34;);
if (str == null) {
return false;
}
paramApplicationInfo = (IPlugin)a(paramApplicationInfo, str, IPlugin.class);
if (paramApplicationInfo == null) {
return false;
}
return paramApplicationInfo.isSupport(MainApplication.b());
}
/*
参数1:插件包
参数2:配置里写入的package名
*/
private static boolean a(File paramFile, String paramString)
{
try
{
boolean bool = Arrays.equals(com.iflytek.utils.hash.b.a(MainApplication.b().getAssets().open(paramString)), com.iflytek.utils.hash.b.a(paramFile));
return bool;
}
catch (Exception paramFile) {}
return false;
}
private ApplicationInfo b()
{
int i = 0;
Object localObject1 = new File(c);
Object localObject2 = new File(d);
if ((((File)localObject1).exists()) && (((File)localObject2).exists()))
{
localObject2 = e.b((File)localObject2);
if (TextUtils.isEmpty((CharSequence)localObject2)) {}
}
for (boolean bool = a((File)localObject1, &#34;plugin/&#34; + (String)localObject2); bool; bool = false)
{
localObject1 = a(c, e);
if ((localObject1 == null) || (!a((ApplicationInfo)localObject1))) {
break;
}
return localObject1;
}
for (;;)
{
try
{
localObject1 = new File(d);
e.a((File)localObject1);
localObject2 = MainApplication.b().getAssets().list(&#34;plugin&#34;);
int j = localObject2.length;
if (i < j)
{
String str = localObject2;
if ((!str.endsWith(&#34;.apk&#34;)) || (str.equals(&#34;empty.apk&#34;))) {
break label217;
}
ApplicationInfo localApplicationInfo = d(str);
if ((localApplicationInfo == null) || (!a(localApplicationInfo))) {
break label217;
}
e.a((File)localObject1, Arrays.asList(new String[] { str }));
return localApplicationInfo;
}
}
catch (IOException localIOException)
{
localIOException.printStackTrace();
}
return null;
label217:
i += 1;
}
}
private static ApplicationInfo d(String paramString)
{
String str1 = c;
String str2 = e;
File localFile = new File(f);
e.a(new File(str2));
e.a(localFile);
localFile.mkdirs();
com.iflytek.aichang.util.b.a(MainApplication.b(), &#34;plugin/&#34; + paramString, str1);
a.a(str1, str2);
return a(str1, str2);
}
/*
这个是核心的接口,构造需求的接口类
*/
public final <T> T a(Class<T> paramClass)
{
ApplicationInfo localApplicationInfo = a();
return a(localApplicationInfo, localApplicationInfo.metaData.getString(paramClass.getSimpleName()), paramClass);
}
/*
对外获取manifest中对应的meta-deta
*/
public final String a(String paramString)
{
return a().metaData.getString(paramString);
}
public final int b(String paramString)
{
return a().metaData.getInt(paramString);
}
public final boolean c(String paramString)
{
return a().metaData.getBoolean(paramString);
}
}
中间都是一些跟package和application相关的API,这里就不多说了插件的两个基本核心就是,插件apk的设计和插件的工具类的实现。 |
|