Files
assistant-android/app/src/main/java/com/gh/common/util/PackageUtils.java
2020-05-28 10:31:37 +08:00

576 lines
23 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.gh.common.util;
import android.app.ActivityManager;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import com.g00fy2.versioncompare.Version;
import com.gh.common.constant.Constants;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.entity.ApkEntity;
import com.gh.gamecenter.entity.GameEntity;
import com.gh.gamecenter.entity.GameUpdateEntity;
import com.gh.gamecenter.manager.PackagesManager;
import com.halo.assistant.HaloApp;
import com.lightgame.download.DownloadEntity;
import com.lightgame.utils.Utils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import androidx.annotation.Nullable;
import androidx.core.content.FileProvider;
public class PackageUtils {
public static final String publicKey = "OpenSSLRSAPublicKey{modulus=a8c4bb5748fec8d5c35db1a7a182d41ba4721a91131a417330af79ef4ddb43f9fa0ff4907b0a613bfe152de0ed8fc1b2e6f94a908aa98a5f7adc1ce814ba7ec919d75d9910bdfd8649b4789da6a90ffb61f0d23ac4f828a78fcd0d6f6120c1c43c1f87f7498a89eb40ca8e32dfc2f9d5c10d612b95192870223674e241e53305abf320d7eed76ded398778576e4db7b17b3bc6a792f13de5e43a6a5fae4276c73e6990ce97f68dff0ec16fc9594f175c8d49cd0d7877340d9de60942ca0efc737e50b6c295dfe0713e4532b4e810e1ea11b702b4a27753e41559cbceb247e7f044ec4e3ab2e8bccd8b9fd71286e63307550bcde86deee95adb8133076269135b,publicExponent=10001}";
/*
* 判断是否可以更新只判断gh_version的大小
*/
public static List<GameUpdateEntity> getUpdateData(GameEntity gameEntity) {
List<GameUpdateEntity> updateList = new ArrayList<>();
// 插件更新
for (ApkEntity apkEntity : gameEntity.getApk()) {
if (isCanUpdate(apkEntity, gameEntity.getId())) {
GameUpdateEntity updateEntity = new GameUpdateEntity();
updateEntity.setId(gameEntity.getId());
updateEntity.setName(gameEntity.getName());
updateEntity.setIcon(gameEntity.getIcon());
updateEntity.setPackageName(apkEntity.getPackageName());
updateEntity.setSize(apkEntity.getSize());
updateEntity.setVersion(apkEntity.getVersion());
updateEntity.setGhVersion(apkEntity.getGhVersion());
updateEntity.setUrl(apkEntity.getUrl());
updateEntity.setPlatform(apkEntity.getPlatform());
updateEntity.setEtag(apkEntity.getEtag());
updateEntity.setBrief(gameEntity.getBrief());
updateEntity.setTag(gameEntity.getTag());
updateEntity.setTagStyle(gameEntity.getTagStyle());
updateEntity.setDownload(gameEntity.getDownload());
updateEntity.setIndexPlugin(gameEntity.getIndexPlugin());
updateEntity.setPluginDesc(gameEntity.getPluginDesc());
updateList.add(updateEntity);
}
}
// 非插件游戏更新
for (ApkEntity apkEntity : gameEntity.getApkNormal()) {
// ghVersion 不存在即是非插件游戏
if (TextUtils.isEmpty(apkEntity.getGhVersion())) {
String versionFromRequest = apkEntity.getVersion();
String versionFromInstalledApp = getVersionByPackage(apkEntity.getPackageName());
// 是否需要显示更新
boolean shouldShowUpdate = apkEntity.getForce();
if (shouldShowUpdate && !TextUtils.isEmpty(versionFromRequest) && !TextUtils.isEmpty(versionFromInstalledApp)) {
// 根据版本判断是否需要更新
shouldShowUpdate = new Version(versionFromRequest).isHigherThan(versionFromInstalledApp);
if (shouldShowUpdate) {
GameUpdateEntity updateEntity = new GameUpdateEntity();
updateEntity.setId(gameEntity.getId());
updateEntity.setName(gameEntity.getName());
updateEntity.setIcon(gameEntity.getIcon());
updateEntity.setPackageName(apkEntity.getPackageName());
updateEntity.setSize(apkEntity.getSize());
updateEntity.setVersion(apkEntity.getVersion());
updateEntity.setGhVersion(apkEntity.getGhVersion());
updateEntity.setUrl(apkEntity.getUrl());
updateEntity.setPlatform(apkEntity.getPlatform());
updateEntity.setEtag(apkEntity.getEtag());
updateEntity.setBrief(gameEntity.getBrief());
updateEntity.setTag(gameEntity.getTag());
updateEntity.setTagStyle(gameEntity.getTagStyle());
updateEntity.setIndexPlugin(gameEntity.getIndexPlugin());
updateEntity.setPluginDesc(gameEntity.getPluginDesc());
updateList.add(updateEntity);
}
}
}
}
return updateList;
}
/*
* 获取meta-data
*/
public static Object getMetaData(Context context, String packageName, String name) {
try {
Bundle metaDate = context.getApplicationContext().getPackageManager().getApplicationInfo(
packageName, PackageManager.GET_META_DATA).metaData;
if (metaDate != null) {
return metaDate.get(name);
}
} catch (NameNotFoundException e) {
// e.printStackTrace();
}
return null;
}
/**
* 获取已安装游戏的光环ID(游戏ID)
*
* @param packageName
* @return
*/
@Nullable
public static Object getGhId(String packageName) {
return getMetaData(HaloApp.getInstance().getApplication(), packageName, "gh_id");
}
/*
* 判断是否是插件包
*/
public static boolean isSignedByGh(Context context, String packageName) {
String signature = getApkSignatureByPackageName(context, packageName);
return publicKey.equals(signature);
}
/*
* 根据包名获取apk的签名信息
*/
public static String getApkSignatureByPackageName(Context context, String packageName) {
try {
PackageInfo packageInfo = context.getApplicationContext().getPackageManager()
.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
Signature[] signatures = packageInfo.signatures;
// 使用幸运破解器破解安卓签名认证可能会出现不用签名也能装的情况,这里有可能是空的
if (signatures[0] != null) {
return parseSignature(signatures[0].toByteArray())[0];
} else {
return null;
}
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return null;
}
public static boolean compareSignatureBetweenInstalledAppWithApk(Context context, String packageName, String apkFilePath) {
try {
Signature sig = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures[0];
// Fuck HUAWEI, 华为系统调用 getPackageArchiveInfo 获取魔羯 apk 的签名时会耗时超过5秒造成 ANR没有找到解决方法
// 如果可以的话尽量避免调用 getPackageArchiveInfo 方法
Signature releaseSig = context.getPackageManager().getPackageArchiveInfo(apkFilePath, PackageManager.GET_SIGNATURES).signatures[0];
return sig.hashCode() == releaseSig.hashCode();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
// 从 APK 中获取公钥(只支持包含 V1 签名的 APK
private static String getApkSignatureFromFile(String apkFilePath) {
try {
ZipFile apkFile = new ZipFile(apkFilePath);
Enumeration<?> entries = apkFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = ((ZipEntry) entries.nextElement());
String entryName = entry.getName();
if (entryName.contains("META-INF") && entryName.endsWith(".RSA")) {
InputStream is = apkFile.getInputStream(entry);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificates(is).toArray()[0];
return cert.getPublicKey().toString();
}
}
return "";
} catch (Exception e) {
return "";
}
}
/*
* 解析签名
*/
private static String[] parseSignature(byte[] signature) {
String[] ret = null;
try {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(
new ByteArrayInputStream(signature));
ret = new String[]{cert.getPublicKey().toString(), cert.getSerialNumber().toString()};
} catch (CertificateException e) {
e.printStackTrace();
}
return ret;
}
/*
* 启动安装应用程序
*/
public static void launchSetup(final Context context, DownloadEntity downloadEntity) {
// 取消状态栏下载完成的通知,若存在
downloadEntity.getMeta().put(Constants.MARK_ALREADY_TRIGGERED_INSTALLATION, "YES");
DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity);
launchSetup(context, downloadEntity.getPath());
}
private static void launchSetup(final Context context, final String path) {
try {
if (isCanLaunchSetup(context, path)) {
context.startActivity(PackageUtils.getInstallIntent(context, path));
} else {
DialogUtils.showPluginDialog(context,
() -> context.startActivity(PackageUtils.getUninstallIntent(context, path)));
}
} catch (Exception e) {
Utils.toast(context, e.getMessage());
}
}
/**
* 根据 path 获取 apk 信息确定处理方式
*
* @return true 为直接唤起系统 PackageInstaller, false 为需要插件化
*/
public static boolean isCanLaunchSetup(Context context, String path) {
String packageName = getPackageNameByPath(context, path);
if (TextUtils.isEmpty(packageName)) {
return true;
}
boolean isContain = PackagesManager.isInstalled(packageName);
if (!isContain) {
return true;
}
boolean isInstalled = isInstalled(context, packageName);
if (!isInstalled) {
return true;
}
// 判断当前已安装应用是否为光环签名
if (publicKey.equals(getApkSignatureByPackageName(context, packageName))) {
return true;
}
// 从 APK 文件里读取公钥,与已安装的公钥比较判断是否一样
String signatureFromApk = getApkSignatureFromFile(path);
if (!TextUtils.isEmpty(signatureFromApk)) {
return signatureFromApk.equals(getApkSignatureByPackageName(context, packageName));
}
// 若已安装的应用的签名与即将要安装的签名一致也返回 true
return compareSignatureBetweenInstalledAppWithApk(context, packageName, path);
}
/*
* 根据路径,获取安装游戏的意图
*/
public static Intent getInstallIntent(Context context, String path) {
return getInstallIntent(context, path, false);
}
public static Intent getInstallIntent(Context context, String path, boolean isInAppUpdate) {
Uri uri = Uri.fromFile(new File(path));
Intent installIntent = new Intent(Intent.ACTION_VIEW);
if ("smartisan".equals(Build.MANUFACTURER)) {
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID, new File(path));
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
installIntent.setDataAndType(uri, "application/vnd.android.package-archive");
installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
// 应用内更新不加 FLAG_ACTIVITY_NEW_TASK 在模拟器上会出现安装完成后安装界面也一并消失的类似闪退的表现
// Application 上下文就更不用说了
if (isInAppUpdate || context instanceof Application) {
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
installIntent.setDataAndType(uri, "application/vnd.android.package-archive");
}
InstallUtils.getInstance(context).addInstall(getPackageNameByPath(context, path));
return installIntent;
}
/*
* 根据包名,获取卸载游戏的意图
*/
public static Intent getUninstallIntent(Context context, String path) {
Intent uninstallIntent = new Intent();
uninstallIntent.setAction(Intent.ACTION_DELETE);
uninstallIntent.addCategory(Intent.CATEGORY_DEFAULT);
String packageName = getPackageNameByPath(context, path);
uninstallIntent.setData(Uri.parse("package:" + packageName));
InstallUtils.getInstance(context).addUninstall(packageName);
return uninstallIntent;
}
/*
* 根据路径获取apk的包名
*/
public static String getPackageNameByPath(Context context, String path) {
PackageManager packageManager = context.getApplicationContext().getPackageManager();
PackageInfo info = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
if (info != null) {
ApplicationInfo appInfo = info.applicationInfo;
return appInfo.packageName;
}
return null;
}
/*
* 根据包名,判断是否已安装该游戏
*
* 注意目测只对能启动的app有效(有桌面图标),对一些没有桌面图标的应用无效(参考应用:魅族游戏框架)
*/
public static boolean isInstalled(Context context, String packageName) {
Intent intent = context.getApplicationContext().getPackageManager().getLaunchIntentForPackage(packageName);
return intent != null;
}
/*
* 获取应用第一次安装的时间
*/
public static long getInstalledTime(Context context, String packageName) {
try {
if (context == null) return 0;
PackageInfo packageInfo = context.getApplicationContext().getPackageManager()
.getPackageInfo(packageName, 0);
return packageInfo.firstInstallTime;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return 0;
}
/*
* 返回光环助手的版本信息
*/
public static String getVersionName() {
return BuildConfig.VERSION_NAME;
}
/*
* 返回光环助手的版本code
*/
public static int getVersionCode() {
return BuildConfig.VERSION_CODE;
}
/*
* 获取apk的版本
*/
public static String getVersionByPackage(String packageName) {
try {
return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).versionName;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return null;
}
/*
* 获取apk的版本
*/
public static Drawable getIconByPackage(Context context, String packageName) {
try {
PackageManager packageManager = context.getApplicationContext().getPackageManager();
return packageManager.getApplicationIcon(packageName);
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return null;
}
/*
* 获取所有已安装的软件的包名、版本(非系统应用)
*/
public static ArrayList<String> getAllPackageName(Context context) {
ArrayList<String> list = new ArrayList<>();
List<PackageInfo> packageInfos = context.getApplicationContext().getPackageManager().getInstalledPackages(0);
for (PackageInfo packageInfo : packageInfos) {
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
if (!context.getPackageName().equals(packageInfo.packageName)) {
list.add(packageInfo.packageName);
}
}
}
return list;
}
public static JSONArray getAppList(Context context) {
JSONArray jsonArray = new JSONArray();
try {
PackageManager pm = context.getPackageManager();
List<PackageInfo> packageInfos = pm.getInstalledPackages(0);
for (PackageInfo packageInfo : packageInfos) {
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", pm.getApplicationLabel(packageInfo.applicationInfo).toString());
jsonObject.put("package", packageInfo.packageName);
jsonObject.put("version", packageInfo.versionName);
jsonArray.put(jsonObject);
}
}
} catch (JSONException e) {
e.printStackTrace();
}
return jsonArray;
}
public static JSONObject getAppBasicInfoByPackageName(String packageName) {
JSONObject jsonObject = new JSONObject();
PackageManager pm = HaloApp.getInstance().getApplication().getPackageManager();
try {
PackageInfo packageInfo = HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
jsonObject.put("name", pm.getApplicationLabel(packageInfo.applicationInfo).toString());
jsonObject.put("package", packageName);
jsonObject.put("version", packageInfo.versionName);
}
return jsonObject;
} catch (JSONException | NameNotFoundException e) {
e.printStackTrace();
return jsonObject;
}
}
/*
* 启动应用
*/
public static void launchApplicationByPackageName(Context context, String packageName) {
try {
Intent intent = context.getApplicationContext().getPackageManager().getLaunchIntentForPackage(packageName);
if (intent != null) {
context.startActivity(intent);
} else {
Utils.toast(context, "启动失败");
}
} catch (Exception e) {
e.printStackTrace();
Utils.toast(context, "启动失败");
}
}
/*
* 根据包名,获取软件名称
*/
public static String getNameByPackageName(Context context, String packageName) {
try {
PackageManager pm = context.getApplicationContext().getPackageManager();
ApplicationInfo applicationInfo = pm.getApplicationInfo(
packageName, 0);
return applicationInfo.loadLabel(pm).toString();
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* todo 统一判断
* <p>
* 判断游戏包(插件包) 是否可以更新
*
* @param apkEntity apkEntity 必须是已安装的游戏
* @param gameId 游戏id
* @return true可以更新 false不可以更新
*/
public static boolean isCanUpdate(ApkEntity apkEntity, String gameId) {
// gh_version: gh + timestamp
String gh_version = (String) PackageUtils.getMetaData(
HaloApp.getInstance().getApplication(),
apkEntity.getPackageName(),
"gh_version");
// gh_version: game id
Object gh_id = PackageUtils.getMetaData(
HaloApp.getInstance().getApplication(),
apkEntity.getPackageName(),
"gh_id");
if (gh_version != null && apkEntity.getGhVersion() != null && gh_id != null) {
gh_version = gh_version.substring(2);
return Long.parseLong(gh_version) < Long.parseLong(apkEntity.getGhVersion())
&& apkEntity.getForce()
&& gh_id.equals(gameId);
}
return false;
}
/**
* todo 统一判断
* <p>
* 判断游戏包是否可以插件化
*
* @param apkEntity apkEntity 必须是已安装的游戏
* @return true可以插件化 false不可以插件化
*/
public static boolean isCanPluggable(ApkEntity apkEntity) {
String gh_id = (String) PackageUtils.getMetaData(
HaloApp.getInstance().getApplication(),
apkEntity.getPackageName(),
"gh_id");
return PackageUtils.isInstalled(HaloApp.getInstance().getApplication(), apkEntity.getPackageName())
&& gh_id == null
&& !TextUtils.isEmpty(apkEntity.getGhVersion())
&& !PackageUtils.isSignedByGh(HaloApp.getInstance().getApplication(), apkEntity.getPackageName());
}
/**
* 获取调用者的进程名
* @param context 调用者的上下文
* @return 进程名
*/
public static String obtainProcessName(Context context) {
final int pid = android.os.Process.myPid();
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> listTaskInfo = am.getRunningAppProcesses();
if (listTaskInfo != null && !listTaskInfo.isEmpty()) {
for (ActivityManager.RunningAppProcessInfo info : listTaskInfo) {
if (info != null && info.pid == pid) {
return info.processName;
}
}
}
return null;
}
}