785 lines
31 KiB
Java
785 lines
31 KiB
Java
package com.gh.common.util;
|
||
|
||
import android.app.ActivityManager;
|
||
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.os.PowerManager;
|
||
import android.text.TextUtils;
|
||
|
||
import androidx.annotation.Nullable;
|
||
import androidx.annotation.WorkerThread;
|
||
|
||
import com.g00fy2.versioncompare.Version;
|
||
import com.gh.common.AppExecutor;
|
||
import com.gh.common.xapk.XapkInstaller;
|
||
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.utils.Utils;
|
||
|
||
import net.dongliu.apk.parser.ApkFile;
|
||
import net.dongliu.apk.parser.bean.ApkMeta;
|
||
|
||
import org.json.JSONArray;
|
||
import org.json.JSONException;
|
||
import org.json.JSONObject;
|
||
|
||
import java.io.BufferedReader;
|
||
import java.io.ByteArrayInputStream;
|
||
import java.io.File;
|
||
import java.io.IOException;
|
||
import java.io.InputStream;
|
||
import java.io.InputStreamReader;
|
||
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.HashMap;
|
||
import java.util.List;
|
||
import java.util.Map;
|
||
import java.util.zip.ZipEntry;
|
||
import java.util.zip.ZipFile;
|
||
|
||
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.setRawIcon(gameEntity.getRawIcon());
|
||
updateEntity.setIconSubscript(gameEntity.getIconSubscript());
|
||
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());
|
||
updateEntity.setFormat(apkEntity.getFormat());
|
||
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());
|
||
updateEntity.setFormat(apkEntity.getFormat());
|
||
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");
|
||
}
|
||
|
||
@Nullable
|
||
public static Map<String, String> getSideLoadedInfo() {
|
||
Context context = HaloApp.getInstance().getApplicationContext();
|
||
|
||
String packageName = null;
|
||
try {
|
||
final PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||
final PackageManager packageManager = context.getPackageManager();
|
||
|
||
if (packageInfo != null && packageManager != null) {
|
||
packageName = packageInfo.packageName;
|
||
|
||
// getInstallSourceInfo requires INSTALL_PACKAGES permission which is only given to system
|
||
// apps.
|
||
final String installerPackageName = packageManager.getInstallerPackageName(packageName);
|
||
|
||
final Map<String, String> sideLoadedInfo = new HashMap<>();
|
||
|
||
if (installerPackageName != null) {
|
||
sideLoadedInfo.put("is_side_loaded", "false");
|
||
// could be amazon, google play etc
|
||
sideLoadedInfo.put("installer_store", installerPackageName);
|
||
} else {
|
||
// if it's installed via adb, system apps or untrusted sources
|
||
sideLoadedInfo.put("is_side_loaded", "true");
|
||
}
|
||
|
||
return sideLoadedInfo;
|
||
}
|
||
} catch (Exception e) {
|
||
Utils.log(e.getLocalizedMessage());
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
|
||
/*
|
||
* 判断是否是插件包
|
||
*/
|
||
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;
|
||
}
|
||
|
||
// TODO 找一个更高效的方式来比较 V2 签名
|
||
public static boolean compareSignatureBetweenInstalledAppWithApk(Context context, String packageName, String apkFilePath) {
|
||
try {
|
||
AppExecutor.getUiExecutor().execute(() -> {
|
||
ToastUtils.toast("安装包校验中,约需要3~5秒,请稍候");
|
||
});
|
||
|
||
// 据 Sentry 统计,刚上架一个周末的包里对这个方法有 700+ 次调用,然后其中一部分会造成 ANR
|
||
Signature sig = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures[0];
|
||
|
||
// 调用 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;
|
||
}
|
||
|
||
/**
|
||
* 根据 path 获取 apk 是否可安装 (全新安装或覆盖安装)
|
||
*
|
||
* 由于部分不存在 V1 签名的大安装包在调用系统 API 获取签名信息时会非常慢,所以请在工作线程里调用此方法
|
||
*
|
||
* @return true 为可直接唤起系统 PackageInstaller, false 为需要插件化
|
||
*/
|
||
@WorkerThread
|
||
public static boolean isInstallable(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 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) {
|
||
|
||
// 部分设备 (已知 vivo 5.1.1) 在调用 packageManager.getPackageArchiveInfo 获取比较大的 APK 文件时会出现 ANR
|
||
// 大于 1G 的 APK 就走另类方法
|
||
if (isDeviceUnableToHandleBigApkFile(path)) {
|
||
// XAPK 不存在 AndroidManifest
|
||
if (path.contains(XapkInstaller.XAPK_EXTENSION_NAME)) {
|
||
return null;
|
||
}
|
||
return getPackageNameByPathAlternative(path);
|
||
}
|
||
|
||
PackageManager packageManager = context.getApplicationContext().getPackageManager();
|
||
PackageInfo info = packageManager.getPackageArchiveInfo(path, 0);
|
||
if (info != null) {
|
||
ApplicationInfo appInfo = info.applicationInfo;
|
||
return appInfo.packageName;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 此设备是否不能调用 packageManager.getPackageArchiveInfo 来获取 APK 信息
|
||
* <p>
|
||
* 部分设备 (已知 vivo 5.1.1 及 5.0.1 的设备) 在调用 packageManager.getPackageArchiveInfo 获取比较大的 APK 文件时会出现 ANR
|
||
*/
|
||
public static boolean isDeviceUnableToHandleBigApkFile(String path) {
|
||
if ("vivo".equals(Build.MANUFACTURER)
|
||
&& Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||
File file = new File(path);
|
||
|
||
if (file != null && file.length() > 1024 * 1024 * 1024) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 从 APK 文件里读包名的另类方法
|
||
* 部分设备 (已知 vivo 5.1.1) 在调用 packageManager.getPackageArchiveInfo 获取比较大的 APK 文件时会出现 ANR
|
||
* <p>
|
||
* 令人迷惑的点:
|
||
* 1. 同样的代码,同样的 APK 在 demo 包里调用 packageManager.getPackageArchiveInfo 并不会 ANR
|
||
* 2. 把 packageManager.getPackageArchiveInfo 放在子线程调用一样会出现 ANR
|
||
* 3. demo 里 manifest 中 application 配置和 targetSdk 也改成与光环一样也不会出现 ANR
|
||
* <p>
|
||
* 大概是光环的某个配置触发了系统的 bug ?
|
||
*/
|
||
private static String getPackageNameByPathAlternative(String path) {
|
||
ApkFile apkParser = null;
|
||
try {
|
||
apkParser = new ApkFile(new File(path));
|
||
ApkMeta apkMeta = apkParser.getApkMeta();
|
||
apkParser.close();
|
||
return apkMeta.getPackageName();
|
||
} catch (Throwable e) {
|
||
e.printStackTrace();
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 根据包名,判断是否已安装该游戏
|
||
*
|
||
* 注意:目测只对能启动的app有效(有桌面图标),对一些没有桌面图标的应用无效(参考应用:魅族游戏框架)
|
||
*/
|
||
public static boolean isInstalled(Context context, String packageName) {
|
||
Intent intent = context.getApplicationContext().getPackageManager().getLaunchIntentForPackage(packageName);
|
||
return intent != null;
|
||
}
|
||
|
||
public static boolean isInstalledFromAllPackage(Context context, String packageName) {
|
||
ArrayList<String> allPackageName = getAllPackageName(context);
|
||
return allPackageName.contains(packageName);
|
||
}
|
||
|
||
/*
|
||
* 获取应用第一次安装的时间
|
||
*/
|
||
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 = getInstalledPackages(context, 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 ArrayList<String> getAllPackageNameIncludeGh(Context context) {
|
||
ArrayList<String> list = new ArrayList<>();
|
||
List<PackageInfo> packageInfos = getInstalledPackages(context, 0);
|
||
for (PackageInfo packageInfo : packageInfos) {
|
||
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
|
||
list.add(packageInfo.packageName);
|
||
}
|
||
}
|
||
return list;
|
||
}
|
||
|
||
/*
|
||
* 获取所有已安装的软件的包名(包括系统应用)
|
||
*/
|
||
public static ArrayList<String> getAllPackageNameIncludeSystemApps(Context context) {
|
||
ArrayList<String> list = new ArrayList<>();
|
||
List<PackageInfo> packageInfos = getInstalledPackages(context, 0);
|
||
for (PackageInfo packageInfo : packageInfos) {
|
||
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 = getInstalledPackages(context, 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);
|
||
try {
|
||
return Long.parseLong(gh_version) < Long.parseLong(apkEntity.getGhVersion()) && apkEntity
|
||
.getForce() && gh_id.equals(gameId);
|
||
} catch (NumberFormatException exception) {
|
||
// gh_id 可能出錯
|
||
exception.printStackTrace();
|
||
return false;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
public static boolean isNonPluginUpdatable(ApkEntity apkEntity, GameEntity gameEntity) {
|
||
// 非插件游戏更新
|
||
// 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());
|
||
updateEntity.setFormat(apkEntity.getFormat());
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
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) {
|
||
try {
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
} catch (Exception e) {
|
||
// 遇到异常了让这次调用正常执行
|
||
e.printStackTrace();
|
||
return BuildConfig.APPLICATION_ID;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
|
||
/**
|
||
* 应用是否在前台运行
|
||
*/
|
||
public static boolean isAppOnForeground(Context context) {
|
||
ActivityManager activityManager = (ActivityManager) context.getApplicationContext()
|
||
.getSystemService(Context.ACTIVITY_SERVICE);
|
||
if (activityManager == null) return false;
|
||
|
||
List<ActivityManager.RunningAppProcessInfo> appProcesses =
|
||
activityManager.getRunningAppProcesses();
|
||
if (appProcesses == null) return false;
|
||
|
||
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||
if (pm == null) return false;
|
||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
|
||
if (!pm.isInteractive()) return false;
|
||
} else {
|
||
if (!pm.isScreenOn()) return false;
|
||
}
|
||
|
||
String packageName = context.getApplicationContext().getPackageName();
|
||
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
|
||
// The name of the process that this object is associated with.
|
||
if (appProcess.processName.equals(packageName) && appProcess.importance
|
||
== ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
/**
|
||
* 在5.1系统手机使用PackageManager获取已安装应用容易发生Package manager has died异常
|
||
* https://stackoverflow.com/questions/13235793/transactiontoolargeeception-when-trying-to-get-a-list-of-applications-installed/30062632#30062632
|
||
*/
|
||
public static List<PackageInfo> getInstalledPackages(Context context, int flags) {
|
||
final PackageManager pm = context.getPackageManager();
|
||
try {
|
||
return pm.getInstalledPackages(flags);
|
||
} catch (Exception ignored) {
|
||
//we don't care why it didn't succeed. We'll do it using an alternative way instead
|
||
}
|
||
// use fallback:
|
||
Process process;
|
||
List<PackageInfo> result = new ArrayList<>();
|
||
BufferedReader bufferedReader = null;
|
||
try {
|
||
process = Runtime.getRuntime().exec("pm list packages");
|
||
bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||
String line;
|
||
while ((line = bufferedReader.readLine()) != null) {
|
||
final String packageName = line.substring(line.indexOf(':') + 1);
|
||
final PackageInfo packageInfo = pm.getPackageInfo(packageName, flags);
|
||
result.add(packageInfo);
|
||
}
|
||
process.waitFor();
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
} finally {
|
||
if (bufferedReader != null)
|
||
try {
|
||
bufferedReader.close();
|
||
} catch (IOException e) {
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
}
|