Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display intent filters of activities, services and broadcast receivers #52

Merged
merged 4 commits into from
Oct 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package fr.xgouchet.packageexplorer.core.utils

import fr.xgouchet.packageexplorer.core.utils.ManifestType.ATTRS
import org.w3c.dom.Document
import org.w3c.dom.NamedNodeMap
import org.w3c.dom.Node
import org.w3c.dom.NodeList

/**
* Created by Ekrem HATİPOĞLU on 11.10.2020.
*/


object ManifestType {
const val APPLICATION = "application"
const val INTENT_FILTER = "intent-filter"
const val ATTRS = "attrs"
}


data class AndroidManifest(val items: List<Item>)
data class Item(val tagName: String, val attrs: Map<String, String>, val childList: List<Item> = listOf())

fun Document.parseDocumentToManifest(): AndroidManifest {
val items = mutableListOf<Item>()

val applicationNode = this.documentElement.getElementsByTagName(ManifestType.APPLICATION.toString())
applicationNode.takeFirst {
childNodes.forEach {
items.add(extractItem())
}
}

return AndroidManifest(items)
}

fun AndroidManifest.filterByName(name: String) = this.items.first { it.attrs.containsValue(name) }

fun List<Item>.formatItem(): List<Map<String, Map<String, String>>> {

val list = mutableListOf<Map<String, Map<String, String>>>()

forEach { intentFilter ->
val item = mutableMapOf<String, Map<String, String>>()
if (intentFilter.attrs.isNotEmpty()){
item[ATTRS] = intentFilter.attrs
}

intentFilter.childList.forEach { child ->
item[child.tagName] = child.attrs
}
list.add(item)
}

return list
}


fun Item.getItemsFromChildByType(type: String): List<Item> {
return childList.filter { it.tagName == type }
}

private fun Node.extractChildren(): List<Item> {
val items = mutableListOf<Item>()
childNodes.forEach {
items.add(extractItem())
}

return items
}

private fun Node.extractAttrs(): Map<String, String> {
val map = hashMapOf<String, String>()
attributes.forEach {
map[nodeName] = nodeValue
}
return map
}

private fun Node.extractItem(): Item {
val tagName = nodeName
val attrs = extractAttrs()
val childList = extractChildren()

return Item(tagName, attrs, childList)
}

private fun NodeList.forEach(block: Node.() -> Unit) {
for (i in 0 until this.length) {
this.item(i).block()
}
}

private fun NamedNodeMap.forEach(block: Node.() -> Unit) {
for (i in 0 until this.length) {
this.item(i).block()
}
}

private fun NodeList.takeFirst(block: Node.() -> Unit) {
if (this.length > 0)
this.item(0).block()
}


Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ fun exportManifestFromPackage(
})
}

fun exportManifestDomFromPackage(
info: PackageInfo
): Document {
return parseManifestFile(getPackageApk(info))
}

fun exportManifestFromApk(
apk: File,
context: Context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,20 @@ package fr.xgouchet.packageexplorer.details

import android.content.ComponentName
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.FeatureInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.Signature
import android.content.pm.*
import android.graphics.drawable.Drawable
import android.os.Build
import fr.xgouchet.packageexplorer.R
import fr.xgouchet.packageexplorer.core.utils.humanReadableName
import fr.xgouchet.packageexplorer.details.adapter.AppInfoHeader
import fr.xgouchet.packageexplorer.details.adapter.AppInfoSimple
import fr.xgouchet.packageexplorer.details.adapter.AppInfoType
import fr.xgouchet.packageexplorer.details.adapter.AppInfoViewModel
import fr.xgouchet.packageexplorer.details.adapter.AppInfoWithIcon
import fr.xgouchet.packageexplorer.details.adapter.AppInfoWithSubtitle
import fr.xgouchet.packageexplorer.details.adapter.AppInfoWithSubtitleAndAction
import fr.xgouchet.packageexplorer.details.adapter.AppInfoWithSubtitleAndIcon
import fr.xgouchet.packageexplorer.core.utils.*
import fr.xgouchet.packageexplorer.details.adapter.*
import io.reactivex.ObservableEmitter
import timber.log.Timber
import java.io.File
import java.security.MessageDigest
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import javax.security.cert.CertificateException
import javax.security.cert.X509Certificate
import timber.log.Timber

open class DetailsSource(val context: Context) {

Expand All @@ -38,11 +27,13 @@ open class DetailsSource(val context: Context) {
private val HEX_CHARS = "0123456789ABCDEF".toCharArray()
}

protected var androidManifestXml: AndroidManifest? = null

protected fun extractMainInfo(
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo,
applicationInfo: ApplicationInfo?,
apkFile: File?
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo,
applicationInfo: ApplicationInfo?,
apkFile: File?
) {
emitter.apply {
onNext(AppInfoWithSubtitle(AppInfoType.INFO_TYPE_METADATA, PACKAGE_NAME, packageInfo.packageName))
Expand Down Expand Up @@ -101,9 +92,9 @@ open class DetailsSource(val context: Context) {
}

protected fun extractActivities(
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo,
packageManager: PackageManager
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo,
packageManager: PackageManager
) {
val activities = packageInfo.activities ?: return
val packageName = packageInfo.packageName
Expand All @@ -122,13 +113,16 @@ open class DetailsSource(val context: Context) {
}

onNext(AppInfoWithSubtitleAndIcon(AppInfoType.INFO_TYPE_ACTIVITIES, label, name, activity.name, icon))

extractIntentFilters(this, AppInfoType.INFO_TYPE_ACTIVITIES, activity.name)

}
}
}

protected fun extractServices(
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo
) {
val services = packageInfo.services ?: return
val packageName = packageInfo.packageName
Expand All @@ -139,13 +133,16 @@ open class DetailsSource(val context: Context) {
for (service in services) {
val simplifiedName = simplifyName(service.name, packageName)
onNext(AppInfoSimple(AppInfoType.INFO_TYPE_SERVICES, simplifiedName, service.name))

extractIntentFilters(this, AppInfoType.INFO_TYPE_SERVICES, service.name)

}
}
}

protected fun extractProviders(
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo
) {
val providers = packageInfo.providers ?: return
val packageName = packageInfo.packageName
Expand All @@ -161,8 +158,8 @@ open class DetailsSource(val context: Context) {
}

protected fun extractReceivers(
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo
) {
val receivers = packageInfo.receivers ?: return
val packageName = packageInfo.packageName
Expand All @@ -173,13 +170,15 @@ open class DetailsSource(val context: Context) {
for (receiver in receivers) {
val simplifiedName = simplifyName(receiver.name, packageName)
onNext(AppInfoSimple(AppInfoType.INFO_TYPE_RECEIVERS, simplifiedName, receiver.name))

extractIntentFilters(this, AppInfoType.INFO_TYPE_RECEIVERS, receiver.name)
}
}
}

protected fun extractCustomPermissions(
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo
) {

val permissions = packageInfo.permissions ?: return
Expand All @@ -202,8 +201,8 @@ open class DetailsSource(val context: Context) {
}

protected fun extractPermissions(
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo
) {
val permissions = packageInfo.requestedPermissions ?: return
val customPermissions = packageInfo.permissions?.toList()?.map { it.name } ?: emptyList()
Expand Down Expand Up @@ -240,8 +239,8 @@ open class DetailsSource(val context: Context) {
}

protected fun extractFeatures(
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo
) {
val features = packageInfo.reqFeatures ?: return

Expand Down Expand Up @@ -273,8 +272,8 @@ open class DetailsSource(val context: Context) {
}

protected fun extractSignatures(
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo
emitter: ObservableEmitter<AppInfoViewModel>,
packageInfo: PackageInfo
) {
val signatures: Array<Signature> = packageInfo.signatures ?: return
if (signatures.isEmpty()) return
Expand All @@ -299,6 +298,23 @@ open class DetailsSource(val context: Context) {
}
}

private fun extractIntentFilters(observableEmitter: ObservableEmitter<AppInfoViewModel>, infoType: Int, name: String) {
androidManifestXml?.let { manifest ->
val parent = manifest.filterByName(name)
val intentFilters= parent.getItemsFromChildByType(ManifestType.INTENT_FILTER)

intentFilters.formatItem().forEach { intentFilter ->
observableEmitter.onNext(AppInfoSubHeader(infoType, ManifestType.INTENT_FILTER))
intentFilter.forEach { (tagName, item) ->
observableEmitter.onNext(AppInfoSubHeader(infoType, tagName))
item.forEach {
observableEmitter.onNext(AppInfoBullet(infoType, it.key, it.value, it.value))
}
}
}
}
}

private fun simplifyName(name: String?, packageName: String?): String {
if (name == null) return "?"
if (packageName == null) return name
Expand Down Expand Up @@ -347,4 +363,8 @@ open class DetailsSource(val context: Context) {
"${HEX_CHARS[firstIndex]}${HEX_CHARS[secondIndex]}"
}
}

}



Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ class AppDetailsAdapter(
return AppInfoWithSubtitleAndActionViewHolder(root, listener, actionListener)
}

TYPE_BULLET -> {
val root = layoutInflater.inflate(R.layout.item_info_bullet, parent, false)
return AppInfoBulletViewHolder(root, listener)
}

TYPE_SUB_HEADER -> {
val root = layoutInflater.inflate(R.layout.item_info_sub_header, parent, false)
return AppInfoSubHeaderViewHolder(root, listener)
}

else -> throw IllegalArgumentException("Unknown view type $viewType")
}
}
Expand All @@ -67,6 +77,8 @@ class AppDetailsAdapter(
is AppInfoWithSubtitle -> return TYPE_SUBTITLE
is AppInfoWithSubtitleAndIcon -> return TYPE_SUBTITLE_ICON
is AppInfoWithSubtitleAndAction -> return TYPE_SUBTITLE_ACTION
is AppInfoBullet -> return TYPE_BULLET
is AppInfoSubHeader -> return TYPE_SUB_HEADER
else -> throw IllegalArgumentException("Unknown type $item")
}
}
Expand All @@ -78,5 +90,7 @@ class AppDetailsAdapter(
private const val TYPE_SUBTITLE = 3
private const val TYPE_SUBTITLE_ICON = 4
private const val TYPE_SUBTITLE_ACTION = 5
private const val TYPE_BULLET = 6
private const val TYPE_SUB_HEADER = 7
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ class AppInfoHeaderViewHolder(
}
}

class AppInfoSubHeaderViewHolder(
itemView: View,
listener: BiConsumer<AppInfoViewModel, View?>?
) : AppInfoViewHolder<AppInfoSubHeader>(itemView, listener) {

private val titleView: TextView = itemView.findViewById(R.id.title)

override fun onBindAppInfoItem(item: AppInfoSubHeader) {
titleView.text = item.header
}
}

class AppInfoSimpleViewHolder(
itemView: View,
listener: BiConsumer<AppInfoViewModel, View?>?
Expand All @@ -63,6 +75,24 @@ class AppInfoSimpleViewHolder(
}
}

class AppInfoBulletViewHolder(
itemView: View,
listener: BiConsumer<AppInfoViewModel, View?>?
) : AppInfoViewHolder<AppInfoBullet>(itemView, listener) {

private val iconView: ImageView = itemView.findViewById(R.id.icon)
private val nameView: TextView = itemView.findViewById(R.id.name)
private val valueView: TextView = itemView.findViewById(R.id.value)
private val separatorView: TextView = itemView.findViewById(R.id.separator)

override fun onBindAppInfoItem(item: AppInfoBullet) {
nameView.text = item.name
valueView.text = item.value
separatorView.text = item.separator
iconView.setImageResource(item.icon)
}
}

class AppInfoWithIconViewHolder(
itemView: View,
listener: BiConsumer<AppInfoViewModel, View?>?
Expand Down
Loading