Commit fe591e7d authored by liuyang's avatar liuyang

APK图片抓取识别及轮播流地址抓取,添加协议串联

#BYLSERVER-1438
parent 30946a2f
......@@ -35,6 +35,8 @@ def uploadServiceVersion = "3.4.2"
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation(name: 'appbase-release', ext: 'aar')
implementation 'com.android.volley:volley:1.1.0'
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'org.nanohttpd:nanohttpd:2.2.0'
implementation 'com.yanzhenjie:andserver:1.1.4'
......
......@@ -3,5 +3,9 @@
"packageName": "com.elinkway.tvlive2",
"launcher": "com.elinkway.tvlive2.activity.SplashActivity",
"launchDelay": "15",
"keyEvents": "down|3/ok"
"captureDelay": "15",
"channelKeyEvent": "down",
"channelCount": "10",
"mediaExt": "m3u8|.mp4",
"menuKeyEvent": "ok"
}
\ No newline at end of file
package com.duolebo.blyrobot.data
import com.duolebo.appbase.IModel
import com.duolebo.appbase.prj.XMLHelper
import org.json.JSONArray
import org.json.JSONObject
class ApkInfo: IModel {
//包名
lateinit var packageName: String
//启动apk配置信息
lateinit var launcher: String
//截图存放路径
lateinit var imagePath: String
// 过滤媒体视频扩展名信息
lateinit var mediaExt: String
// 播放地址过滤url
lateinit var filterUrl: String
// 抓取频道数量
var channelCount = 5
// 启动延时(秒)
var launchDelay = 15
// 频道抓取延时(秒)
var captureDelay = 15
// 切换频道按键事件组合
lateinit var channelKeyEvent: String
// 弹出界面菜单按键,默认确定键
lateinit var menuKeyEvent: String
override fun from(json: JSONObject): Boolean {
this.packageName = json.optString(PACKAGE_NAME)
this.launcher = json.optString(LAUNCHER)
this.launchDelay = json.optInt(LAUNCH_DELAY, 15)
this.captureDelay = json.optInt(CAPTURE_DELAY, 15)
this.channelCount = json.optInt(CHANNEL_COUNT, 5)
this.channelKeyEvent = json.optString(CHANNEL_KEY_EVENT)
this.mediaExt = json.optString(MEDIA_EXT, VIDEO_EXTS)
this.filterUrl = json.optString(FILTER_URL)
this.menuKeyEvent = json.optString(MENU_KEY_EVENT, "ok")
return true
}
override fun from(p0: JSONArray?): Boolean {
return false
}
override fun from(p0: XMLHelper?): Boolean {
return false
}
companion object {
const val VIDEO_EXTS = "m3u8|.ts|.mp4|.rmvb|.mkv|.wmv"
//data key
const val PACKAGE_NAME = "packageName"
const val LAUNCHER = "launcher"
const val LAUNCH_DELAY = "launchDelay"
const val CAPTURE_DELAY = "captureDelay"
const val CHANNEL_KEY_EVENT = "channelKeyEvent"
const val CHANNEL_COUNT = "channelCount"
const val MEDIA_EXT = "mediaExt"
const val FILTER_URL = "filterUrl"
const val MENU_KEY_EVENT = "menuKeyEvent"
}
}
\ No newline at end of file
package com.duolebo.blyrobot.data
import com.duolebo.appbase.IModel
import com.duolebo.appbase.prj.XMLHelper
import org.json.JSONArray
import org.json.JSONObject
class ApkReportData: IModel {
override fun from(p0: JSONObject?): Boolean {
return true
}
override fun from(p0: JSONArray?): Boolean {
return false
}
override fun from(p0: XMLHelper?): Boolean {
return false
}
}
\ No newline at end of file
package com.duolebo.blyrobot.data
import com.duolebo.appbase.IModel
import com.duolebo.appbase.prj.XMLHelper
import org.json.JSONArray
import org.json.JSONObject
class AppInfoData : IModel {
val apkInfos = ArrayList<ApkInfo>()
override fun from(json: JSONObject): Boolean {
val data = json.optJSONArray("data")
if (data != null) {
for (i in 0..data.length()) {
val apkInfo = ApkInfo()
apkInfo.from(data.getJSONObject(i))
apkInfos.add(apkInfo)
}
}
return true
}
override fun from(json: JSONArray?): Boolean {
return true
}
override fun from(p0: XMLHelper?): Boolean {
return false
}
}
\ No newline at end of file
......@@ -2,25 +2,35 @@ package com.duolebo.blyrobot.data
import android.content.Context
import android.util.Log
import com.duolebo.appbase.AppBaseHandler
import com.duolebo.appbase.IAppBaseCallback
import com.duolebo.appbase.IProtocol
import com.duolebo.blyrobot.protocol.ApkReportProtocol
import com.duolebo.blyrobot.util.AdbUtil
import com.duolebo.blyrobot.util.AppUtil
import com.duolebo.blyrobot.util.Constants
import org.json.JSONObject
import com.duolebo.blyrobot.util.Config
import net.gotev.uploadservice.UploadNotificationConfig
import net.gotev.uploadservice.ftp.FTPUploadRequest
import org.json.JSONArray
import org.json.JSONObject
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.ArrayList
class Task : IAppBaseCallback {
class Task {
lateinit var packageName: String
private lateinit var launcher: String
private val TAG = "Task"
lateinit var apkInfo: ApkInfo
//截图存放路径
private lateinit var imagePath: String
private var launchDelay = 15
private var captureDelay = 15
private lateinit var keyEvent: String
private var channelIndex = 0
private var reportProtocol: ApkReportProtocol
private var dataHandler:AppBaseHandler
private lateinit var reportJson: JSONObject
private var context: Context
private val imageDateFormat = SimpleDateFormat("yyyy_MM_dd_HH_mm_ss", Locale.CHINA)
......@@ -28,66 +38,83 @@ class Task {
constructor(context: Context) {
this.context = context
this.reportProtocol = ApkReportProtocol(context, Config.instance)
this.dataHandler = AppBaseHandler(this)
}
fun from(json: JSONObject) {
this.packageName = json.optString(Constants.PACKAGE_NAME)
this.launcher = json.optString(Constants.LAUNCHER)
this.launchDelay = json.optInt(Constants.LAUNCH_DELAY, 15)
this.imagePath = this.context.cacheDir.absolutePath + "/" + this.packageName
this.keyEvent = json.optString(Constants.KEY_EVENT)
fun from(apkInfo: ApkInfo) {
this.imagePath = this.context.cacheDir.absolutePath + "/" + apkInfo.packageName
val dir = File(this.imagePath)
if (!dir.exists())
dir.mkdirs()
}
fun start() {
this.reportJson = JSONObject()
this.launchApp()
this.capture()
this.processChannels()
this.uploadImage()
this.report()
stop()
}
fun stop() {
AdbUtil.stopApp(this.packageName)
private fun finish(result: Boolean) {
AdbUtil.stopApp(this.apkInfo.packageName)
this.taskListener?.run {
onComplete()
onComplete(result)
}
}
fun launchApp(reset: Boolean = false) {
private fun launchApp(reset: Boolean = false) {
Log.i(TAG, "launchApp")
if (reset) {
try {
AdbUtil.resetApp(this.packageName)
AdbUtil.resetApp(this.apkInfo.packageName)
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
AdbUtil.launchApp("$packageName/$launcher")
Thread.sleep(launchDelay * 1000L)
AdbUtil.launchApp("${this.apkInfo.packageName}/${this.apkInfo.launcher}")
Thread.sleep(this.apkInfo.launchDelay * 1000L)
}
private fun processChannels() {
Log.i(TAG, "processChannels")
step()
for (i in 1..this.apkInfo.channelCount) {
// 模拟按键事件. 切换频道进行抓取
this.channelIndex = i
AdbUtil.sendMultiKey(this.apkInfo.channelKeyEvent)
step()
}
}
private fun step() {
capture()
// 截图保存
val screenShots = saveScreenShot()
val playUrlItems = analysisCapture()
}
// 抓包处理
private fun capture() {
Log.i(TAG, "capture")
Thread(Runnable {
proc = AdbUtil.tcpCapture("")
}).start()
Thread.sleep(captureDelay * 1000L)
Log.i(TAG, "capture sleep ${this.apkInfo.captureDelay} seconds")
Thread.sleep(this.apkInfo.captureDelay * 1000L)
proc?.destroy()
AdbUtil.killTcpdump()
Thread.sleep(3000L)
parseCaptureFile("")
Thread.sleep(2000L)
}
private fun report() {
}
private fun parseCaptureFile(filterUrl: String) {
// 分析http抓包文件
private fun analysisCapture(): ArrayList<PlayInfo> {
val playUrlItems = ArrayList<PlayInfo>()
val file = File("/sdcard/capture.txt")
val lines = file.readLines()
val playUrlItems = ArrayList<PlayInfo>()
var partUrl = ""
var timeStr = ""
......@@ -108,61 +135,113 @@ class Task {
val start = it.indexOf(' ') + 1
val host = it.substring(start)
val url = "http://$host$partUrl"
val item = PlayInfo()
item.url = url
item.time = timeStr
playUrlItems.add(item)
var add = true
// 如果筛选url不为空,这里需要进行过滤
if (!this.apkInfo.filterUrl.isNullOrEmpty()) {
if (url.contains(this.apkInfo.filterUrl))
add = false
}
if (add) {
// 这里进行媒体视频播放地址识别
val extArr = this.apkInfo.mediaExt.split("|")
for (ext in extArr) {
if (url.contains(ext)) {
add = true
break
}
}
}
if (add) {
val item = PlayInfo()
item.url = url
item.time = timeStr
playUrlItems.add(item)
}
partUrl = ""
}
}
for (i in 0..playUrlItems.size) {
val item = playUrlItems.get(i)
return playUrlItems
}
if (filterUrl.isNullOrEmpty()) {
// 截取一张频道图+一张显示频道节目的图
private fun saveScreenShot(): ArrayList<String> {
val screenImages = ArrayList<String>()
}
else {
var time = imageDateFormat.format(Date())
val absName = this.imagePath + "/${this.apkInfo.packageName}_${channelIndex}_$time"
screenShot(absName)
screenImages.add("$absName.jpg")
}
}
AdbUtil.sendMultiKey(this.apkInfo.menuKeyEvent)
time = imageDateFormat.format(Date())
val absOkName = this.imagePath + "/${this.apkInfo.packageName}_${channelIndex}_${time}_ok"
screenShot(absOkName)
screenImages.add("$absOkName.jpg")
return screenImages
}
fun screenShot() {
val time = imageDateFormat.format(Date())
var pngPath = this.imagePath + "/${packageName}_$time.png"
// 截图处理,转成jpg
private fun screenShot(absName : String) {
val pngPath = "/$absName.png"
val jpgPath = pngPath.replace(".png", ".jpg")
AdbUtil.screenShot(pngPath)
// png转换成jpg
AppUtil.pngToJpg(pngPath, jpgPath)
}
fun uploadImage() {
private fun uploadImage() {
try {
val uploadRequest = FTPUploadRequest(context, "ftp.duolebo.com", 21)
.setUsernameAndPassword("user", "password")
.setNotificationConfig(UploadNotificationConfig())
.setMaxRetries(4)
var dir = File(imagePath)
val dir = File(imagePath)
dir.listFiles().forEach {
uploadRequest.addFileToUpload(it.absolutePath, "/remote/path")
}
val uploadId = uploadRequest.startUpload()
Log.i(TAG, "upload id $uploadId")
} catch (exc: Exception) {
Log.e("AndroidUploadService", exc.message, exc)
}
}
private fun report() {
reportProtocol.execute(dataHandler)
}
override fun onProtocolFailed(p0: IProtocol?) {
finish(false)
}
override fun onHttpFailed(p0: IProtocol?) {
finish(false)
}
override fun onProtocolSucceed(p0: IProtocol?) {
finish(true)
}
var taskListener: OnTaskListener? = null
interface OnTaskListener {
fun onComplete()
fun onComplete(result: Boolean)
}
class PlayInfo {
var time = ""
var url = ""
}
}
\ No newline at end of file
package com.duolebo.blyrobot.protocol
import android.content.Context
import com.duolebo.appbase.IModel
import com.duolebo.appbase.prj.bmtv.protocol.IProtocolConfig
import com.duolebo.appbase.prj.bmtv.protocol.ProtocolBase
import com.duolebo.blyrobot.data.ApkReportData
class ApkReportProtocol(context: Context?, config: IProtocolConfig?) : ProtocolBase(context, config) {
val model = ApkReportData()
override fun prepareProtocolBody(p0: MutableMap<String, String>?) {
}
override fun getData(): IModel {
return model
}
override fun prepareProtocolRequestKey(): String {
return "http://test.duolebo.com/staging/apkReport/report.do"
}
}
\ No newline at end of file
package com.duolebo.blyrobot.protocol
class GetAppConfig {
}
\ No newline at end of file
package com.duolebo.blyrobot.protocol
import android.content.Context
import com.duolebo.appbase.IModel
import com.duolebo.appbase.prj.bmtv.protocol.IProtocolConfig
import com.duolebo.appbase.prj.bmtv.protocol.ProtocolBase
import com.duolebo.blyrobot.data.AppInfoData
class GetAppInfoProtocol(context: Context?, config: IProtocolConfig?) : ProtocolBase(context, config) {
val model = AppInfoData()
override fun prepareProtocolBody(p0: MutableMap<String, String>?) {
}
override fun getData(): IModel {
return model
}
override fun prepareProtocolRequestKey(): String {
return ""
}
override fun prepareHttpRequestUrl(): String {
return "http://test.duolebo.com/staging/apkInfo/query.do"
}
}
\ No newline at end of file
......@@ -45,7 +45,7 @@ class BylRobotService: Service() {
timer!!.schedule(object: TimerTask() {
override fun run() {
// testKey()
testCapture()
// testCapture()
// screenShot()
count++
if (count > 2) {
......
package com.duolebo.blyrobot.util
import com.duolebo.appbase.prj.bmtv.protocol.IProtocolConfig
class Config: IProtocolConfig {
override fun getProtocolUrl(): String {
return ""
}
override fun getTvid(): String {
return ""
}
override fun getChannel(): String {
return ""
}
companion object {
val instance = Config()
}
}
\ No newline at end of file
package com.duolebo.blyrobot.util
object Constants {
const val KEY_URL = "url"
const val KEY_TIME = "time"
const val KEY_CMD = "cmd"
const val CMD_START = "start"
......@@ -31,13 +29,6 @@ object Constants {
"mute" to "164")
const val ONLY_VIDEO = "only_video"
val VIDEO_EXTS = "m3u8|.ts|.mp4|.rmvb|.mkv|.wmv"
//data key
const val PACKAGE_NAME = "packageName"
const val LAUNCHER = "launcher"
const val LAUNCH_DELAY = "launchDelay"
const val KEY_EVENT = "keyEvent"
const val ASTERISK = "*"
const val SLASH = "/"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment