解决飞鸽传输2.3.6在安卓11/13上无法调用apk安装器的问题
本帖最后由 侃遍天下无二人 于 2023-8-26 15:25 编辑【修复所写代码已开源,见 https://gitee.com/kbtxwer/feige-fix/】
上回书说到:我们通过修改返回值去掉了飞鸽传输的广告,但飞鸽传输毕竟是很古老的软件了,在安卓11上虽然还基本能正常运行,但无法通过它直接安装收到的apk,这会给我的使用带来些许不便
原本还可以利用ES文件中转站解决,但我最近破解的旧版ES文件浏览器没这个功能,于是只好上网找找安卓11上安装软件的新方法,尝试融进飞鸽传输中:
根据 Android Apk安装(兼容Android11 Api30)中的描述,安卓11废弃了 Intent.ACTION_INSTALL_PACKAGE 字段,使以前的安装方法不再可用
文中还给了示例代码,不过引入kt会让安装包体积大增,因此要转换为java的实现。
用jadx打开上回修改好的飞鸽传输,定位到 com.tj.feige.app.a.e 的577行附近,发现一个静态无返回值的b(Context context, String str)方法,这就是我们要改的方法了:
可以看出它在无条件调用 android.intent.action.VIEW,而我们要让这个方法在传入的文件后缀为apk,且安卓版本不小于11(sdk 30)时,走我们新增的逻辑(代码不能在jadx里直接修改,手动改smail也不现实,所以我在这里用Android Studio重新建立简化的开发环境写代码)
package com.tj.feige.app.a;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import com.kbtx.AppInstaller;
import java.io.File;
import java.io.IOException;
public class e {
private static String a(File file){
return "";
};
private static void a(Context context, String str){}
@SuppressLint("WrongConstant")
public static void b(Context context, String str){
File file = new File(str);
Intent intent = new Intent();
intent.addFlags(268435456);
if(file.getName().endsWith(".apk") && Build.VERSION.SDK_INT >= 30){
intent.setClass(context, AppInstaller.class);
}else {
intent.setAction("android.intent.action.VIEW");
}
intent.setDataAndType(Uri.fromFile(file),a(file));
context.startActivity(intent);
}
}
我们在Android Studio里新建的类路径应当和飞鸽传输里原有的完全一致,这样就可以直接从编译出来的dex中提取对应的smail汇编直接覆盖,如果这个方法调用了dex里的其他函数,只要建一个空壳,然后假装调用就行,确保编译过程不出错。
注意到代码里调用了 AppInstaller.class,这是我仿照文章用Java实现的安装器,代码如下:
package com.kbtx;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageInstaller;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class AppInstaller extends Activity implements View.OnClickListener {
private static final String action = "com.kbtx.Install_APK";
private boolean should_kill = false;
@Override
protected void onCreate(@nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri uri = getIntent().getData();
should_kill = false;
if (uri != null && uri.getScheme().equals("file")) {
File file = new File(uri.getPath());
try {
Install(file);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
protected void onResume() {
super.onResume();
if(should_kill) finish();
should_kill = true;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if(intent==null || !intent.getAction().equals(action)) return;
int status = intent.getExtras().getInt(PackageInstaller.EXTRA_STATUS);
switch (status){
case PackageInstaller.STATUS_PENDING_USER_ACTION:{
startActivity((Intent) intent.getExtras().get(Intent.EXTRA_INTENT));
break;
}
case PackageInstaller.STATUS_SUCCESS:{
Log.i("FeiGe","应用安装成功");
break;
}
case PackageInstaller.STATUS_FAILURE:{
Log.i("FeiGe","应用安装失败");
break;
}
}
}
public void Install(File apk) throws IOException {
if(Build.VERSION.SDK_INT < 30) return;
PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
int sessionId = packageInstaller.createSession(params);
PackageInstaller.Session session = packageInstaller.openSession(sessionId);
addApkToInstallSession(apk,session);
Intent intent = new Intent(this,AppInstaller.class);
intent.setAction(action);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);
IntentSender statRecv = pendingIntent.getIntentSender();
session.commit(statRecv);
}
private void addApkToInstallSession(File apk, PackageInstaller.Session session){
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
Log.e("feige","准备为新版安卓设备安装apk...");
Log.e("feige","安装包路径: " + apk.getAbsolutePath());
try(OutputStream packageInSession = session.openWrite("package",0,-1);
InputStream is = new FileInputStream(apk)
){
byte[] buffer = new byte;
int n;
while((n = is.read(buffer)) >= 0){
packageInSession.write(buffer,0,n);
}
}catch (IOException e){
e.printStackTrace();
Log.e("feige","传输进程出错!");
return;
}
Log.e("feige","数据传输完毕");
}
}
@Override
public void onClick(View view) {
finish();
}
}
此部分代码在清单文件中对应的声明如下:
<activity android:name=".AppInstaller" android:launchMode="singleTop"
android:exported="false">
<intent-filter>
<action android:name="com.kbtx.Install_APK" android:exported="true"/>
</intent-filter>
</activity>
代码写完后,直接构建apk,找到其中的dex文件,将 AppInstaller.smail放到飞哥传输中对应的位置(保证包名和路径一致),将 e.smail中的 b方法覆盖飞鸽传输原有的。
(注:此步建议把dex传输到手机上操作,否则需要用apktool手动拆包封包,没有mt会员可以用np代替)
接下来就是修改飞鸽传输的清单文件,在里面新增对AppInstaller的声明:
<activity android:name="com.kbtx.AppInstaller" android:launchMode="singleTop"
android:exported="false">
<intent-filter>
<action android:name="com.kbtx.Install_APK" android:exported="true"/>
</intent-filter>
</activity>
同时加入允许安装未知应用的权限:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.REPLACE_EXISTING_PACKAGE" />
然后重新编译生成apk,得到的飞鸽传输就能在安卓11下直接安装接收到的apk文件了
原版:https://wwd.lanzoue.com/ipchmuqmqaj
上次的去广告成品:https://wwd.lanzoue.com/iWKLcuqmqba
修复安装功能的成品:https://wwd.lanzoue.com/ixoJ10ewts2b
修复安装功能(用了另一套代码)+底部菜单的成品:https://wwd.lanzoue.com/i4PzS0g88tcj
修复所写代码已开源,见 https://gitee.com/kbtxwer/feige-fix/
hekygogo 发表于 2023-8-26 15:08
谢谢楼主,一直用的飞鸽2007,可惜安卓的不行,找了很多跨平台互传软件feem landrop都不顺手,这个修改版ap ...
这两天升级到安卓13了,发现照样能正常运行 zoomyou 发表于 2022-11-1 09:53
飞鸽传输,有PC版吗,是不是这个手机软件和PC可以互通联系的?
Windows上有ipmsg或者飞秋,Linux上有iptux,都是可以互通的 本帖最后由 ouzhzh 于 2022-10-31 16:48 编辑
飞鸽传输在手机上效果如何?使用过的说一下。 楼主的技术不错,佩服,下载来试试 此贴最大的败笔是没有配图,殊不知红花儿配绿叶? 感谢分享 谢谢分享 感谢分享 冥界3大法王 发表于 2022-10-31 17:18
此贴最大的败笔是没有配图,殊不知红花儿配绿叶?
补上了,看看还有哪些细节需要补充的 侃遍天下无二人 发表于 2022-10-31 19:14
补上了,看看还有哪些细节需要补充的
飞鸽传书的界面最好来一张,这样才承托主人公。@侃遍天下无二人 不然总觉得没有主角。。。 楼主厉害,老物件焕发新活力