827 lines
34 KiB
Kotlin
827 lines
34 KiB
Kotlin
package com.gh.common.util
|
||
|
||
import android.annotation.SuppressLint
|
||
import android.content.Context
|
||
import android.content.pm.PackageInfo
|
||
import android.content.pm.PackageManager
|
||
import android.content.pm.PermissionInfo
|
||
import android.os.Build
|
||
import android.os.Process
|
||
import android.provider.Settings
|
||
import androidx.fragment.app.FragmentActivity
|
||
import androidx.lifecycle.LiveData
|
||
import androidx.lifecycle.MutableLiveData
|
||
import com.gh.common.constant.Config
|
||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||
import com.gh.gamecenter.common.utils.DialogHelper
|
||
import com.gh.gamecenter.common.utils.PermissionHelper
|
||
import com.gh.gamecenter.common.utils.SensorsBridge
|
||
import com.gh.gamecenter.common.utils.toJson
|
||
import com.gh.gamecenter.common.utils.toObject
|
||
import com.gh.gamecenter.core.GHThreadFactory
|
||
import com.gh.gamecenter.core.runOnUiThread
|
||
import com.gh.gamecenter.core.utils.SPUtils
|
||
import com.gh.gamecenter.entity.WhitePackageListEntity
|
||
import com.gh.gamecenter.feature.entity.GameEntity
|
||
import com.gh.gamecenter.feature.entity.SettingsEntity
|
||
import com.gh.gamecenter.feature.utils.SentryHelper
|
||
import com.gh.gamecenter.manager.PackagesManager
|
||
import com.gh.gamecenter.packagehelper.PackageRepository
|
||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||
import com.halo.assistant.HaloApp
|
||
import com.lightgame.utils.Utils
|
||
import io.reactivex.schedulers.Schedulers
|
||
import kotlinx.coroutines.*
|
||
import java.util.HashMap
|
||
import java.util.concurrent.Executors
|
||
import kotlin.collections.ArrayList
|
||
import kotlin.collections.HashSet
|
||
|
||
object PackageHelper {
|
||
|
||
private const val TAG = "PackageHelper"
|
||
|
||
private const val SP_GET_INSTALLED_PACKAGES_AGREED = "get_installed_packages_agreed" // 用户是否同意使用已安装应用列表 API
|
||
|
||
private const val SP_GET_INSTALLED_PACKAGES_BY_ALTERNATIVE_API =
|
||
"get_installed_packages_by_alternative_api" // 是否使用另类方式获取已安装应用列表
|
||
|
||
private const val SP_USER_USED_GET_INSTALLED_API_SWITCH_PACKAGE =
|
||
"user_used_get_installed_api_switch_package" // 用户是否使用过含有已安装应用列表获取开关的包
|
||
|
||
private const val SP_ADDITIONAL_WHITELIST_PACKAGE_NAME_LIST =
|
||
"additional_whitelist_package_name_list" // 额外的白名单包名列表 (曾经更正为已安装的包名列表)
|
||
|
||
private const val UNKNOWN = -1
|
||
private const val UNSUPPORTED = 0
|
||
private const val SUPPORTED = 1
|
||
|
||
private const val ENABLED = 2
|
||
private const val DISABLED = 3
|
||
|
||
private var lastSuccessfullyGetInstalledPackagesTimeMills = 0L
|
||
private var cachedInstalledPackageInfoList: List<PackageInfo> = arrayListOf() // 缓存的已安装应用列表
|
||
private var cachedInstalledPackageNameList: List<String> = arrayListOf() // 缓存的已安装应用包名列表
|
||
private var additionalWhiteListPackageNameSet: HashSet<String> = hashSetOf()
|
||
|
||
private var isGetInstalledPackagesAgreed = false // 用户是否已经同意使用已安装应用列表
|
||
private var isGetInstalledPackagesAgreedRequired = UNKNOWN // 需要用户手动授权才获取已安装应用列表的功能的开关
|
||
private var isGetInstalledPackagesPermissionSupported = UNKNOWN // 设备是否支持禁用获取已安装应用列表
|
||
|
||
private var useAlternativeWayToGetInstalledPackages = false
|
||
|
||
private var cachedPkgNameAndGameEntityMap = HashMap<String, GameEntity>() // 缓存的状态异常包名和游戏实体的映射 (用于免接口请求更新状态)
|
||
|
||
private val packageExecutor by lazy { Executors.newSingleThreadExecutor(GHThreadFactory("GH_PACKAGE_THREAD")) }
|
||
|
||
private var uploadUIDGapLog = true
|
||
|
||
// 光环运行的环境是否为默认的 UID (0)
|
||
private val isRunningOnDefaultUid by lazy { Process.myUid() / 100000 == 0 }
|
||
|
||
// 评论黑名单包名列表,避免用户安装了 Xposed Installer 这样的工具,也能在包含该安装包的游戏详情页评论
|
||
private var _commentPackageNameBlackList = arrayListOf<String>()
|
||
val commentPackageNameBlackList: ArrayList<String>
|
||
get() = _commentPackageNameBlackList
|
||
|
||
// 关闭下载的包列表
|
||
private var _downloadPackageNameBlackList = arrayListOf<String>()
|
||
val downloadPackageNameBlackList: ArrayList<String>
|
||
get() = _downloadPackageNameBlackList
|
||
|
||
// 本地已安装的包去掉关闭下载的包后的列表
|
||
private var _validLocalPackageNameSet = hashSetOf<String>()
|
||
val validLocalPackageNameSet: HashSet<String>
|
||
get() = _validLocalPackageNameSet
|
||
|
||
// 游戏包名匹配列表
|
||
private var _relatedPackageList = arrayListOf<SettingsEntity.GameWithPackages>()
|
||
val relatedPackageList: ArrayList<SettingsEntity.GameWithPackages>
|
||
get() = _relatedPackageList
|
||
|
||
// 接口控制的已安装应用列表获取开关状态 (UI 显示)
|
||
private var _installedPackageApiSwitchStatusLiveData = MutableLiveData<Boolean>()
|
||
val installedPackageApiSwitchStatusLiveData: LiveData<Boolean>
|
||
get() = _installedPackageApiSwitchStatusLiveData
|
||
|
||
// 本地已安装包的列表
|
||
var localPackageNameSet = hashSetOf<String>()
|
||
get() {
|
||
return if (field.isEmpty()) {
|
||
field = getAllPackageName(HaloApp.getInstance().application)
|
||
field
|
||
} else {
|
||
field
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查是否需要忽略接口控制的已安装应用列表获取开关
|
||
* 用于覆盖安装首次启动时的检查 https://jira.shanqu.cc/browse/GHZSCY-5694
|
||
*/
|
||
fun checkIfGetInstalledApiSwitchShouldBeIgnored(context: Context) {
|
||
val userHasUsedGetInstalledApiSwitchPackage =
|
||
SPUtils.getBoolean(SP_USER_USED_GET_INSTALLED_API_SWITCH_PACKAGE, false)
|
||
if (!userHasUsedGetInstalledApiSwitchPackage) {
|
||
val isSupportGetInstalledAppsPermission = isSupportGetInstalledAppsPermission(context)
|
||
|
||
if (!isSupportGetInstalledAppsPermission) {
|
||
if (isRunningOnDefaultUid) {
|
||
// 设备不支持动态管理获取已安装应用列表,忽略接口控制,使用另类方式获取已安装应用列表
|
||
updateUseAlternativeWayToGetInstalledPackages()
|
||
}
|
||
onGetInstalledPackagesAgreed()
|
||
} else if (!PermissionHelper.isGetInstalledListPermissionDisabled(context)) {
|
||
if (isRunningOnDefaultUid) {
|
||
// 设备支持动态管理获取已安装应用列表但已经授权,忽略接口控制,使用另类方式获取已安装应用列表
|
||
updateUseAlternativeWayToGetInstalledPackages()
|
||
}
|
||
onGetInstalledPackagesAgreed()
|
||
}
|
||
|
||
SPUtils.getBoolean(SP_USER_USED_GET_INSTALLED_API_SWITCH_PACKAGE, true)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取包名白名单列表(为了在没有已安装应用列表获取能力的时候也能正常判断更新、插件化)
|
||
* @param additionalWhiteList 额外的已安装白名单
|
||
*/
|
||
@SuppressLint("CheckResult")
|
||
fun getPackagesWhiteList(additionalWhiteList: HashSet<String>) {
|
||
RetrofitManager.getInstance().newApi.installWhitelist
|
||
.subscribeOn(Schedulers.io())
|
||
.subscribe(object : BiResponse<WhitePackageListEntity>() {
|
||
override fun onSuccess(data: WhitePackageListEntity) {
|
||
val installedWhiteList = hashSetOf<String>()
|
||
|
||
if (additionalWhiteList.isNotEmpty()) {
|
||
installedWhiteList.addAll(additionalWhiteList)
|
||
}
|
||
|
||
data.data?.let {
|
||
installedWhiteList.addAll(it)
|
||
}
|
||
|
||
addInstalledButMissingPackages(installedWhiteList)
|
||
}
|
||
|
||
override fun onFailure(exception: Exception) {
|
||
super.onFailure(exception)
|
||
|
||
if (additionalWhiteList.isNotEmpty()) {
|
||
addInstalledButMissingPackages(additionalWhiteList)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
@JvmStatic
|
||
fun refreshLocalPackageList() {
|
||
localPackageNameSet = getAllPackageName(HaloApp.getInstance().application)
|
||
updateValidPackageNameList()
|
||
}
|
||
|
||
@JvmStatic
|
||
fun refreshPackageNameList() {
|
||
Config.getSettings()?.gameCommentBlackList?.let { _commentPackageNameBlackList = ArrayList(it) }
|
||
Config.getSettings()?.gameDownloadBlackList?.let { _downloadPackageNameBlackList = ArrayList(it) }
|
||
Config.getSettings()?.gamePackageMatch?.let { _relatedPackageList = ArrayList(it) }
|
||
|
||
updateValidPackageNameList()
|
||
}
|
||
|
||
private fun updateValidPackageNameList() {
|
||
_validLocalPackageNameSet =
|
||
localPackageNameSet.filterNot { p -> downloadPackageNameBlackList.contains(p) }.toHashSet()
|
||
}
|
||
|
||
/**
|
||
* 获取所有已安装的软件的包名、版本(非系统应用)
|
||
*/
|
||
private fun getAllPackageName(context: Context): HashSet<String> {
|
||
val set = HashSet<String>()
|
||
return try {
|
||
val packageNameList = getInstalledPackageNameList(context, 0)
|
||
for (packageName in packageNameList) {
|
||
if (context.packageName != packageName) {
|
||
set.add(packageName)
|
||
}
|
||
}
|
||
set
|
||
} catch (e: Exception) {
|
||
e.printStackTrace()
|
||
set
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 弃用已安装列表缓存
|
||
*/
|
||
fun dumpInstalledListCache() {
|
||
lastSuccessfullyGetInstalledPackagesTimeMills = 0
|
||
}
|
||
|
||
/**
|
||
* 在超时后,若后台没有开启获取已安装应用列表的功能,回落到以接口不控制的方式获取已安装应用列表
|
||
*/
|
||
fun ignoreInstalledPackageApiSwitchAfterTimeout(timeout: Long) {
|
||
CoroutineScope(SupervisorJob()).launch {
|
||
delay(timeout)
|
||
if (isGetInstalledPackagesAgreedRequired == UNKNOWN && !isGetInstalledPackagesAgreed()) {
|
||
Utils.log(TAG, "后台没有开启获取已安装应用列表的功能,超时后回落到以接口不控制的方式获取已安装应用列表")
|
||
updateIsGetInstalledPackagesApiAgreedRequired(false)
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 更新已安装应用列表获取开关状态
|
||
*/
|
||
fun updateIsGetInstalledPackagesApiAgreedRequired(isEnabled: Boolean) {
|
||
Utils.log(TAG, "updateIsGetInstalledPackagesApiAgreedRequired 入参为 $isEnabled")
|
||
|
||
// 状态不变,无需更新
|
||
if ((isEnabled == true && isGetInstalledPackagesAgreedRequired == ENABLED)
|
||
|| (isEnabled == false && isGetInstalledPackagesAgreedRequired == DISABLED)
|
||
) {
|
||
Utils.log(TAG, "isGetInstalledPackagesApiAgreedRequired 状态不变,无需更新")
|
||
return
|
||
}
|
||
|
||
// 若用户已经同意使用了,无需更新
|
||
if (isGetInstalledPackagesAgreed()) {
|
||
Utils.log(TAG, "用户已经同意使用了,无需再更新已安装应用列表获取开关状态")
|
||
return
|
||
}
|
||
|
||
if (isEnabled) {
|
||
Utils.log(TAG, "开启获取已安装应用列表限制")
|
||
|
||
additionalWhiteListPackageNameSet =
|
||
SPUtils.getString(SP_ADDITIONAL_WHITELIST_PACKAGE_NAME_LIST).toObject() ?: hashSetOf()
|
||
|
||
Utils.log(TAG, "额外的白名单为 $additionalWhiteListPackageNameSet")
|
||
|
||
getPackagesWhiteList(additionalWhiteListPackageNameSet)
|
||
_installedPackageApiSwitchStatusLiveData.postValue(true)
|
||
isGetInstalledPackagesAgreedRequired = ENABLED
|
||
} else {
|
||
Utils.log(TAG, "不开启获取已安装应用列表限制")
|
||
|
||
isGetInstalledPackagesAgreedRequired = DISABLED
|
||
|
||
if (isRunningOnDefaultUid) {
|
||
// 启用另类获取已安装应用列表的 API
|
||
updateUseAlternativeWayToGetInstalledPackages()
|
||
}
|
||
|
||
onGetInstalledPackagesAgreed()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 用户是否已经允许了调用获取已安装应用列表接口
|
||
* 优先用内存的值,没有再从 SP 中获取并更新
|
||
*/
|
||
fun isGetInstalledPackagesAgreed(): Boolean {
|
||
return isGetInstalledPackagesAgreed
|
||
|| (SPUtils.getBoolean(SP_GET_INSTALLED_PACKAGES_AGREED).also { isGetInstalledPackagesAgreed = it })
|
||
}
|
||
|
||
/**
|
||
* 用户是否已经使用另类方式获取已安装应用列表
|
||
*/
|
||
private fun isUseAlternativeWayToGetInstalledPackages(): Boolean {
|
||
return useAlternativeWayToGetInstalledPackages
|
||
|| (SPUtils.getBoolean(SP_GET_INSTALLED_PACKAGES_BY_ALTERNATIVE_API)
|
||
.also { useAlternativeWayToGetInstalledPackages = it })
|
||
}
|
||
|
||
private fun updateUseAlternativeWayToGetInstalledPackages() {
|
||
// 启用另类获取已安装应用列表的 API
|
||
useAlternativeWayToGetInstalledPackages = true
|
||
SPUtils.setBoolean(SP_GET_INSTALLED_PACKAGES_BY_ALTERNATIVE_API, true)
|
||
}
|
||
|
||
fun isGetInstalledPackagesAgreedRequired(): Boolean {
|
||
return isGetInstalledPackagesAgreedRequired == ENABLED
|
||
}
|
||
|
||
/**
|
||
* 获取已安装应用列表
|
||
*/
|
||
fun getInstalledPackageInfoList(context: Context?, flags: Int): List<PackageInfo> {
|
||
return getInstalledListInternal(context, flags, false).first
|
||
}
|
||
|
||
/**
|
||
* 获取已安装应用包名列表
|
||
*/
|
||
fun getInstalledPackageNameList(context: Context?, flags: Int): List<String> {
|
||
return getInstalledListInternal(context, flags, true).second
|
||
}
|
||
|
||
private fun getInstalledListInternal(context: Context?,
|
||
flags: Int,
|
||
packageNameOnly: Boolean): Pair<List<PackageInfo>, List<String>> {
|
||
Utils.log(TAG, "即将获取已安装应用列表,仅获取包名 $packageNameOnly")
|
||
|
||
// 用户未同意使用已安装应用列表 API,返回空列表
|
||
if (!isGetInstalledPackagesAgreed()) {
|
||
Utils.log(TAG, "用户未同意使用已安装应用列表 API,返回空列表")
|
||
return Pair(cachedInstalledPackageInfoList, cachedInstalledPackageNameList)
|
||
}
|
||
|
||
// 简单 debounce 过于频繁的获取已安装应用列表调用
|
||
if (System.currentTimeMillis() - lastSuccessfullyGetInstalledPackagesTimeMills < 20 * 1000) {
|
||
// 时间间隔合适且对应的列表不为空,直接返回对应的缓存列表数据
|
||
if ((packageNameOnly && cachedInstalledPackageNameList.isNotEmpty())
|
||
|| (!packageNameOnly && cachedInstalledPackageInfoList.isNotEmpty())
|
||
) {
|
||
Utils.log(TAG, "使用了缓存的已安装应用列表")
|
||
return Pair(cachedInstalledPackageInfoList, cachedInstalledPackageNameList)
|
||
}
|
||
}
|
||
|
||
var shouldGetNewInstalledPackagedList = false
|
||
|
||
// 当前设备是否支持限制获取已安装应用列表的功能
|
||
if (isSupportGetInstalledAppsPermission(context!!)) {
|
||
Utils.log(TAG, "当前设备支持动态管理获取已安装应用列表的功能")
|
||
// 当前设备是否支持禁用了获取已安装应用列表
|
||
if (!PermissionHelper.isGetInstalledListPermissionDisabled(context)) {
|
||
Utils.log(TAG, "当前设备支持动态管理但没有限制获取已安装应用列表的功能")
|
||
shouldGetNewInstalledPackagedList = true
|
||
} else {
|
||
Utils.log(TAG, "当前设备支持动态管理且已限制获取已安装应用列表的功能")
|
||
}
|
||
} else {
|
||
Utils.log(TAG, "当前设备不支持动态管理获取已安装应用列表的功能")
|
||
shouldGetNewInstalledPackagedList = true
|
||
}
|
||
|
||
if (shouldGetNewInstalledPackagedList) {
|
||
lastSuccessfullyGetInstalledPackagesTimeMills = System.currentTimeMillis()
|
||
|
||
if (packageNameOnly) {
|
||
cachedInstalledPackageNameList = getInstalledPackageNameListInternal(context, flags)
|
||
Utils.log(TAG, "获取已安装应用列表成功,数量为 ${cachedInstalledPackageNameList.size}")
|
||
} else {
|
||
cachedInstalledPackageInfoList = getInstalledPackageInfoListInternal(context, flags)
|
||
Utils.log(TAG, "获取已安装应用列表成功,数量为 ${cachedInstalledPackageInfoList.size}")
|
||
}
|
||
}
|
||
|
||
return Pair(cachedInstalledPackageInfoList, cachedInstalledPackageNameList)
|
||
}
|
||
|
||
/**
|
||
* 显示获取已安装应用列表的对话框并请求权限
|
||
*/
|
||
fun showGetInstallAppsListDialogAndRequestPermissionIfNeeded(
|
||
activity: FragmentActivity,
|
||
ignorePermanentlyDenied: Boolean = false,
|
||
resultClosure: (Boolean) -> Unit
|
||
) {
|
||
|
||
if (isSupportGetInstalledAppsPermission(activity)) {
|
||
// 若系统已经授予了获取应用列表的权限,直接执行授权成功回调
|
||
if (!PermissionHelper.isGetInstalledListPermissionDisabled(activity)) {
|
||
onGetInstalledPackagesAgreed()
|
||
resultClosure.invoke(true)
|
||
return
|
||
}
|
||
|
||
PermissionHelper.showGetInstalledAppsListPermissionDialog(
|
||
activity = activity,
|
||
requestPermission = true,
|
||
ignorePermanentlyDenied = ignorePermanentlyDenied
|
||
) { isGranted ->
|
||
if (isGranted) {
|
||
SensorsBridge.trackInstalledListPermissionsResult("成功")
|
||
onGetInstalledPackagesAgreed()
|
||
resultClosure.invoke(true)
|
||
|
||
trackInstalledListAfterDelay()
|
||
} else {
|
||
resultClosure.invoke(false)
|
||
SensorsBridge.trackInstalledListPermissionsResult("拒绝")
|
||
}
|
||
}
|
||
} else {
|
||
val hintDialog = PermissionHelper.showGetInstalledAppsListPermissionDialog(
|
||
activity = activity,
|
||
requestPermission = false,
|
||
) {
|
||
// do nothing
|
||
}
|
||
|
||
SensorsBridge.trackInstalledListPermissionsCustomDialogShow()
|
||
|
||
val noticeDialog = DialogHelper.showGuideDialog(
|
||
context = activity,
|
||
title = "权限申请",
|
||
content = "是否允许“光环助手”获取已安装的应用信息",
|
||
confirmText = "开启",
|
||
cancelText = "拒绝",
|
||
confirmClickCallback = {
|
||
SensorsBridge.trackInstalledListPermissionsCustomClick("开启")
|
||
onGetInstalledPackagesAgreed()
|
||
resultClosure.invoke(true)
|
||
|
||
trackInstalledListAfterDelay()
|
||
},
|
||
cancelClickCallback = {
|
||
resultClosure.invoke(false)
|
||
SensorsBridge.trackInstalledListPermissionsCustomClick("拒绝")
|
||
}
|
||
)
|
||
|
||
noticeDialog?.setOnDismissListener {
|
||
hintDialog?.dismiss()
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 执行用户授权获取已安装应用列表的操作
|
||
*/
|
||
private fun onGetInstalledPackagesAgreed() {
|
||
isGetInstalledPackagesAgreed = true
|
||
SPUtils.setBoolean(SP_GET_INSTALLED_PACKAGES_AGREED, true)
|
||
|
||
_installedPackageApiSwitchStatusLiveData.postValue(false)
|
||
|
||
// 进行包名初始化相关的操作
|
||
initPackageRelatedData()
|
||
}
|
||
|
||
/**
|
||
* 进行包名初始化相关的操作
|
||
*/
|
||
fun initPackageRelatedData() {
|
||
PackageRepository.initData()
|
||
refreshLocalPackageList()
|
||
refreshPackageNameList()
|
||
}
|
||
|
||
/**
|
||
* 延迟5秒后上报已安装应用列表
|
||
*/
|
||
private fun trackInstalledListAfterDelay() {
|
||
CoroutineScope(SupervisorJob()).launch {
|
||
delay(5000)
|
||
SensorsBridge.trackNumberOfInstalledList(localPackageNameSet.size, localPackageNameSet)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 是否支持动态获取已安装应用列表权限
|
||
*/
|
||
fun isSupportGetInstalledAppsPermission(context: Context): Boolean {
|
||
if (isUseAlternativeWayToGetInstalledPackages()) {
|
||
// 已经使用另类获取已安装应用列表形式,强制判定为不支持动态获取已安装应用列表权限
|
||
return false
|
||
}
|
||
|
||
// 若存在缓存,直接返回缓存结果。
|
||
if (isGetInstalledPackagesPermissionSupported != UNKNOWN) {
|
||
return isGetInstalledPackagesPermissionSupported != UNSUPPORTED
|
||
}
|
||
|
||
try {
|
||
// 根据官方提供的方法来判定是否支持限制获取已安装应用列表
|
||
val flag =
|
||
Settings.Secure.getInt(context.contentResolver, "oem_installed_apps_runtime_permission_enable", 0)
|
||
if (flag == 1) {
|
||
isGetInstalledPackagesPermissionSupported = SUPPORTED
|
||
return true
|
||
}
|
||
|
||
// 部分未升级的手机没有上面配置项,有定义下面危险权限也认为是支持设备软件列表管控
|
||
val packageManager = context.packageManager
|
||
val permissionInfo = packageManager.getPermissionInfo("com.android.permission.GET_INSTALLED_APPS", 0)
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||
if (permissionInfo.protection == PermissionInfo.PROTECTION_DANGEROUS) {
|
||
isGetInstalledPackagesPermissionSupported = SUPPORTED
|
||
return true
|
||
} else {
|
||
isGetInstalledPackagesPermissionSupported = UNSUPPORTED
|
||
return false
|
||
}
|
||
} else {
|
||
isGetInstalledPackagesPermissionSupported = UNSUPPORTED
|
||
return false
|
||
}
|
||
} catch (e: PackageManager.NameNotFoundException) {
|
||
isGetInstalledPackagesPermissionSupported = UNSUPPORTED
|
||
return false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 确保指定包名的应用在已安装了的情况下能正常收录
|
||
*/
|
||
fun addInstalledButMissingPackages(packageNameSet: HashSet<String>) {
|
||
Utils.log(TAG, "addInstalledButMissingPackages 检查已安装但未收录的应用")
|
||
|
||
val installedPackageNameSet: HashSet<String> = hashSetOf()
|
||
|
||
for (packageName in packageNameSet) {
|
||
val installedPkgNameExistInMemory = PackagesManager.isInstalled(packageName)
|
||
val packageNameInstalledOnDevice = PackageUtils.getVersionNameByPackageName(packageName) != null
|
||
|
||
if (!installedPkgNameExistInMemory && packageNameInstalledOnDevice) {
|
||
installedPackageNameSet.add(packageName)
|
||
}
|
||
|
||
if (!packageNameInstalledOnDevice) {
|
||
additionalWhiteListPackageNameSet.remove(packageName)
|
||
}
|
||
}
|
||
|
||
SPUtils.setString(SP_ADDITIONAL_WHITELIST_PACKAGE_NAME_LIST, additionalWhiteListPackageNameSet.toJson())
|
||
|
||
Utils.log(TAG, "addInstalledButMissingPackages 需要请求接口获取的包数量为 ${installedPackageNameSet.size}")
|
||
|
||
PackageRepository.addInstalledGames(
|
||
packageFilterManager = PackageRepository.packageFilterManager,
|
||
pkgNameList = ArrayList(installedPackageNameSet),
|
||
updateInstallStatus = true
|
||
)
|
||
}
|
||
|
||
/**
|
||
* 更新额外的白名单包名列表
|
||
* @param packageName 包名
|
||
* @param isAdd 是否添加
|
||
*/
|
||
fun updateAdditionalWhiteListPackageName(packageName: String, isAdd: Boolean) {
|
||
val isUpdated = if (isAdd) {
|
||
additionalWhiteListPackageNameSet.add(packageName)
|
||
} else {
|
||
additionalWhiteListPackageNameSet.remove(packageName)
|
||
}
|
||
|
||
Utils.log(
|
||
TAG,
|
||
"updateAdditionalWhiteListPackageName 更新额外的白名单包名列表 $isAdd $packageName ,结果是 $isUpdated"
|
||
)
|
||
|
||
if (isUpdated) {
|
||
SPUtils.setString(SP_ADDITIONAL_WHITELIST_PACKAGE_NAME_LIST, additionalWhiteListPackageNameSet.toJson())
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 刷新安装状态异常 (适用于仅存在包名信息的列表)
|
||
*/
|
||
fun refreshWrongInstallStatus(packageNameSet: MutableSet<String>) {
|
||
packageExecutor.execute {
|
||
Utils.log(TAG, "refreshWrongInstallStatus 检查安装状态异常的应用")
|
||
|
||
val uninstalledButKeepingWrongStatusPackageNameSet: HashSet<String> = hashSetOf()
|
||
val updatedButKeepingWrongStatusPackageNameSet: HashSet<String> = hashSetOf()
|
||
|
||
for (packageName in packageNameSet) {
|
||
val installedVersionName = PackageUtils.getVersionNameByPackageName(packageName)
|
||
|
||
if (PackagesManager.isInstalled(packageName)
|
||
&& installedVersionName == null
|
||
) {
|
||
uninstalledButKeepingWrongStatusPackageNameSet.add(packageName)
|
||
} else if (PackagesManager.isInstalled(packageName)
|
||
&& installedVersionName != null
|
||
&& !PackagesManager.isInstalledWithSpecificVersion(packageName, installedVersionName)
|
||
) {
|
||
updatedButKeepingWrongStatusPackageNameSet.add(packageName)
|
||
}
|
||
}
|
||
|
||
Utils.log(
|
||
TAG,
|
||
"refreshWrongInstallStatus 需要更新已更新状态的包数量为 ${updatedButKeepingWrongStatusPackageNameSet.size}"
|
||
)
|
||
Utils.log(
|
||
TAG,
|
||
"refreshWrongInstallStatus 需要移除已安装的包数量为 ${uninstalledButKeepingWrongStatusPackageNameSet.size}"
|
||
)
|
||
|
||
runOnUiThread {
|
||
if (uninstalledButKeepingWrongStatusPackageNameSet.isNotEmpty()) {
|
||
for (packageName in uninstalledButKeepingWrongStatusPackageNameSet) {
|
||
PackageChangeHelper.addUninstall(packageName)
|
||
additionalWhiteListPackageNameSet.remove(packageName)
|
||
}
|
||
|
||
SPUtils.setString(
|
||
SP_ADDITIONAL_WHITELIST_PACKAGE_NAME_LIST,
|
||
additionalWhiteListPackageNameSet.toString()
|
||
)
|
||
}
|
||
|
||
if (updatedButKeepingWrongStatusPackageNameSet.isNotEmpty()) {
|
||
for (packageName in updatedButKeepingWrongStatusPackageNameSet) {
|
||
PackageChangeHelper.addUpdate(packageName)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 刷新安装状态异常 (适用于存在游戏信息的列表)
|
||
*/
|
||
fun refreshWrongInstallStatus(gameEntityList: ArrayList<GameEntity>) {
|
||
packageExecutor.execute {
|
||
Utils.log(TAG, "refreshWrongInstallStatus 检查安装状态异常的应用")
|
||
|
||
val installedButKeepingWrongStatusPackageNameSet: HashSet<String> = hashSetOf()
|
||
val uninstalledButKeepingWrongStatusPackageNameSet: HashSet<String> = hashSetOf()
|
||
val updatedButKeepingWrongStatusPackageNameSet: HashSet<String> = hashSetOf()
|
||
|
||
for (game in gameEntityList) {
|
||
for (apk in game.getApk()) {
|
||
val packageName = apk.packageName
|
||
val installedVersionName = PackageUtils.getVersionNameByPackageName(packageName)
|
||
|
||
if (!PackagesManager.isInstalled(packageName)
|
||
&& installedVersionName != null
|
||
) {
|
||
cachedPkgNameAndGameEntityMap.put(packageName, game)
|
||
installedButKeepingWrongStatusPackageNameSet.add(packageName)
|
||
} else if (PackagesManager.isInstalled(packageName)
|
||
&& installedVersionName == null
|
||
) {
|
||
uninstalledButKeepingWrongStatusPackageNameSet.add(packageName)
|
||
} else if (PackagesManager.isInstalled(packageName)
|
||
&& installedVersionName != null
|
||
&& !PackagesManager.isInstalledWithSpecificVersion(packageName, installedVersionName)
|
||
&& !PackagesManager.isCanUpdate(game.id, packageName, false)
|
||
) {
|
||
cachedPkgNameAndGameEntityMap.put(packageName, game)
|
||
updatedButKeepingWrongStatusPackageNameSet.add(packageName)
|
||
}
|
||
}
|
||
}
|
||
|
||
Utils.log(
|
||
TAG,
|
||
"refreshWrongInstallStatus 需要更新已安装状态的包数量为 ${installedButKeepingWrongStatusPackageNameSet.size}"
|
||
)
|
||
Utils.log(
|
||
TAG,
|
||
"refreshWrongInstallStatus 需要更新已更新状态的包数量为 ${updatedButKeepingWrongStatusPackageNameSet.size}"
|
||
)
|
||
Utils.log(
|
||
TAG,
|
||
"refreshWrongInstallStatus 需要移除已安装的包数量为 ${uninstalledButKeepingWrongStatusPackageNameSet.size}"
|
||
)
|
||
|
||
runOnUiThread {
|
||
if (installedButKeepingWrongStatusPackageNameSet.isNotEmpty()) {
|
||
for (packageName in installedButKeepingWrongStatusPackageNameSet) {
|
||
PackageChangeHelper.addInstall(packageName, cachedPkgNameAndGameEntityMap.remove(packageName))
|
||
additionalWhiteListPackageNameSet.add(packageName)
|
||
}
|
||
|
||
SPUtils.setString(
|
||
SP_ADDITIONAL_WHITELIST_PACKAGE_NAME_LIST,
|
||
additionalWhiteListPackageNameSet.toString()
|
||
)
|
||
}
|
||
|
||
if (uninstalledButKeepingWrongStatusPackageNameSet.isNotEmpty()) {
|
||
for (packageName in uninstalledButKeepingWrongStatusPackageNameSet) {
|
||
PackageChangeHelper.addUninstall(packageName)
|
||
additionalWhiteListPackageNameSet.remove(packageName)
|
||
}
|
||
|
||
SPUtils.setString(
|
||
SP_ADDITIONAL_WHITELIST_PACKAGE_NAME_LIST,
|
||
additionalWhiteListPackageNameSet.toString()
|
||
)
|
||
}
|
||
|
||
if (updatedButKeepingWrongStatusPackageNameSet.isNotEmpty()) {
|
||
for (packageName in updatedButKeepingWrongStatusPackageNameSet) {
|
||
PackageChangeHelper.addUpdate(packageName, cachedPkgNameAndGameEntityMap.remove(packageName))
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private fun getInstalledPackageInfoListInternal(
|
||
context: Context,
|
||
flags: Int
|
||
): List<PackageInfo> {
|
||
return if (isUseAlternativeWayToGetInstalledPackages()) {
|
||
Utils.log(TAG, "调用另类系统 API 获取已安装应用列表")
|
||
getInstalledPackageByAlternative(context)
|
||
} else {
|
||
Utils.log(TAG, "调用默认系统 API 获取已安装应用列表")
|
||
getInstalledPackageByDefault(context, flags)
|
||
}
|
||
}
|
||
|
||
private fun getInstalledPackageNameListInternal(
|
||
context: Context,
|
||
flags: Int
|
||
): List<String> {
|
||
if (isUseAlternativeWayToGetInstalledPackages()) {
|
||
Utils.log(TAG, "调用另类系统 API 获取已安装应用包名列表")
|
||
return getInstalledPackageNameByAlternative(context)
|
||
} else {
|
||
Utils.log(TAG, "调用默认系统 API 获取已安装应用包名列表")
|
||
val packageInfoList = getInstalledPackageByDefault(context, flags)
|
||
val packageList = arrayListOf<String>()
|
||
for (packageInfo in packageInfoList) {
|
||
if (context.packageName != packageInfo.packageName) {
|
||
packageList.add(packageInfo.packageName)
|
||
}
|
||
}
|
||
return packageList
|
||
}
|
||
}
|
||
|
||
private fun getInstalledPackageByDefault(context: Context, flags: Int): List<PackageInfo> {
|
||
val pm = context.packageManager
|
||
try {
|
||
return pm.getInstalledPackages(flags)
|
||
} catch (e: java.lang.Exception) {
|
||
e.printStackTrace()
|
||
}
|
||
|
||
return arrayListOf()
|
||
}
|
||
|
||
private fun getInstalledPackageByAlternative(context: Context): ArrayList<PackageInfo> {
|
||
val packageManager = context.getPackageManager()
|
||
val packageList = arrayListOf<PackageInfo>()
|
||
var packagesArray: Array<String>? = null
|
||
var uid = android.os.Process.FIRST_APPLICATION_UID
|
||
|
||
while (uid <= android.os.Process.LAST_APPLICATION_UID) {
|
||
try {
|
||
packagesArray = packageManager.getPackagesForUid(uid)
|
||
if (packagesArray != null && packagesArray.isNotEmpty()) {
|
||
for (packageName in packagesArray) {
|
||
try {
|
||
val packageInfo = packageManager.getPackageInfo(packageName, 0)
|
||
if (packageInfo == null) {
|
||
break
|
||
}
|
||
|
||
packageList.add(packageInfo)
|
||
} catch (e: PackageManager.NameNotFoundException) {
|
||
e.printStackTrace()
|
||
}
|
||
}
|
||
}
|
||
} catch (securityException: SecurityException) {
|
||
securityException.printStackTrace()
|
||
}
|
||
uid++
|
||
}
|
||
|
||
return packageList
|
||
}
|
||
|
||
private fun getInstalledPackageNameByAlternative(context: Context): ArrayList<String> {
|
||
val packageManager = context.getPackageManager()
|
||
val packageList = arrayListOf<String>()
|
||
var packagesArray: Array<String>? = null
|
||
var uid = android.os.Process.FIRST_APPLICATION_UID
|
||
|
||
var lastValidUid = 0
|
||
|
||
while (uid <= android.os.Process.LAST_APPLICATION_UID) {
|
||
try {
|
||
packagesArray = packageManager.getPackagesForUid(uid)
|
||
if (packagesArray != null && packagesArray.isNotEmpty()) {
|
||
lastValidUid = uid
|
||
|
||
for (packageName in packagesArray) {
|
||
packageList.add(packageName)
|
||
}
|
||
}
|
||
} catch (securityException: SecurityException) {
|
||
securityException.printStackTrace()
|
||
}
|
||
uid++
|
||
}
|
||
|
||
if (HaloApp.getInstance().isNewForThisVersion && uploadUIDGapLog) {
|
||
// 仅应用该版本第一次启动的第一次方法调用上报日志
|
||
uploadUIDGapLog = false
|
||
|
||
val uidGap = (android.os.Process.LAST_APPLICATION_UID - lastValidUid) / 100 * 100
|
||
SentryHelper.onEvent("UID_GAP", "gap", uidGap.toString())
|
||
}
|
||
|
||
return packageList
|
||
}
|
||
|
||
} |