From 3ccab0291193043089401e4f10a55b89ea2fb5ba Mon Sep 17 00:00:00 2001 From: wanghongenpin Date: Sat, 24 Feb 2024 22:18:18 +0800 Subject: [PATCH] Optimize app list reading --- .../com/network/proxy/ProxyVpnService.kt | 4 +- .../com/network/proxy/plugin/AppInfo.kt | 59 ++++++++++++++ .../proxy/plugin/InstalledAppsPlugin.kt | 79 ++++++------------- .../proxy/vpn/socket/CloseableConnection.kt | 2 +- lib/native/installed_apps.dart | 5 +- lib/network/channel.dart | 18 +++-- lib/network/proxy_helper.dart | 2 +- lib/network/util/socket_address.dart | 15 ++++ lib/storage/histories.dart | 1 + 9 files changed, 115 insertions(+), 70 deletions(-) create mode 100644 android/app/src/main/kotlin/com/network/proxy/plugin/AppInfo.kt create mode 100644 lib/network/util/socket_address.dart diff --git a/android/app/src/main/kotlin/com/network/proxy/ProxyVpnService.kt b/android/app/src/main/kotlin/com/network/proxy/ProxyVpnService.kt index cad1246..9c7aa42 100644 --- a/android/app/src/main/kotlin/com/network/proxy/ProxyVpnService.kt +++ b/android/app/src/main/kotlin/com/network/proxy/ProxyVpnService.kt @@ -27,6 +27,8 @@ class ProxyVpnService : VpnService(), ProtectSocket { companion object { const val MAX_PACKET_LEN = 1500 + const val VirtualHost = "10.0.0.2" + const val ProxyHost = "ProxyHost" const val ProxyPort = "ProxyPort" const val AllowApps = "AllowApps" //允许的名单 @@ -155,7 +157,7 @@ class ProxyVpnService : VpnService(), ProtectSocket { ParcelFileDescriptor? { val build = Builder() .setMtu(MAX_PACKET_LEN) - .addAddress("10.0.0.2", 32) + .addAddress(VirtualHost, 32) .addRoute("0.0.0.0", 0) .setSession(baseContext.applicationInfo.name) .setBlocking(true) diff --git a/android/app/src/main/kotlin/com/network/proxy/plugin/AppInfo.kt b/android/app/src/main/kotlin/com/network/proxy/plugin/AppInfo.kt new file mode 100644 index 0000000..a28686c --- /dev/null +++ b/android/app/src/main/kotlin/com/network/proxy/plugin/AppInfo.kt @@ -0,0 +1,59 @@ +package com.network.proxy.plugin + +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import java.io.ByteArrayOutputStream + +class AppInfo(name: CharSequence, packageName: String, icon: ByteArray, versionName: String) : + HashMap() { + init { + put("name", name) + put("packageName", packageName) + put("icon", icon) + put("versionName", versionName) + } + companion object { + fun create( + packageManager: PackageManager, + app: ApplicationInfo, + withIcon: Boolean = true + ): AppInfo { + val name = packageManager.getApplicationLabel(app) + val packageName = app.packageName + val icon = + if (withIcon) drawableToByteArray(app.loadIcon(packageManager)) else ByteArray(0) + val packageInfo = packageManager.getPackageInfo(app.packageName, 0) + val versionName = packageInfo.versionName + + return AppInfo(name, packageName, icon, versionName) + } + + private fun drawableToByteArray(drawable: Drawable): ByteArray { + val bitmap = drawableToBitmap(drawable) + val stream = ByteArrayOutputStream() + bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream) + return stream.toByteArray() + } + + private fun drawableToBitmap(drawable: Drawable): Bitmap { + if (drawable is BitmapDrawable) { + return drawable.bitmap + } + val bitmap = Bitmap.createBitmap( + drawable.intrinsicWidth, + drawable.intrinsicHeight, + Bitmap.Config.ARGB_8888 + ) + val canvas = Canvas(bitmap) + drawable.setBounds(0, 0, canvas.width, canvas.height) + drawable.draw(canvas) + return bitmap + } + + } + +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/network/proxy/plugin/InstalledAppsPlugin.kt b/android/app/src/main/kotlin/com/network/proxy/plugin/InstalledAppsPlugin.kt index 1dce886..9ade047 100644 --- a/android/app/src/main/kotlin/com/network/proxy/plugin/InstalledAppsPlugin.kt +++ b/android/app/src/main/kotlin/com/network/proxy/plugin/InstalledAppsPlugin.kt @@ -1,18 +1,18 @@ package com.network.proxy.plugin import android.content.pm.ApplicationInfo -import android.content.pm.PackageInfo -import android.content.pm.PackageManager -import android.graphics.Bitmap -import android.graphics.Canvas -import android.graphics.drawable.BitmapDrawable -import android.graphics.drawable.Drawable -import android.os.Build import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.MethodChannel -import java.io.ByteArrayOutputStream import java.util.Locale +import java.util.concurrent.Callable +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit +/** + * 已经安装应用列表 + * + * @author wanghongen + */ class InstalledAppsPlugin : AndroidFlutterPlugin() { var channel: MethodChannel? = null @@ -41,17 +41,17 @@ class InstalledAppsPlugin : AndroidFlutterPlugin() { } } - private fun getAppInfo(packageName: String): Map { + private fun getAppInfo(packageName: String): AppInfo { val packageManager = activity.packageManager packageManager.getApplicationInfo(packageName, 0).let { app -> - return convertAppToMap(packageManager, app, true) + return AppInfo.create(packageManager, app, true) } } private fun getInstalledApps( withIcon: Boolean, packageNamePrefix: String - ): List> { + ): List { val packageManager = activity.packageManager var installedApps = packageManager.getInstalledApplications(0) installedApps = @@ -67,52 +67,21 @@ class InstalledAppsPlugin : AndroidFlutterPlugin() { packageNamePrefix.lowercase(Locale.ENGLISH) ) } - return installedApps.map { app -> convertAppToMap(packageManager, app, withIcon) } - } - private fun convertAppToMap( - packageManager: PackageManager, - app: ApplicationInfo, - withIcon: Boolean - ): HashMap { - - val map = HashMap() - map["name"] = packageManager.getApplicationLabel(app) - map["packageName"] = app.packageName - map["icon"] = - if (withIcon) drawableToByteArray(app.loadIcon(packageManager)) else ByteArray(0) - val packageInfo = packageManager.getPackageInfo(app.packageName, 0) - map["versionName"] = packageInfo.versionName - map["versionCode"] = getVersionCode(packageInfo) - return map - } - - private fun drawableToByteArray(drawable: Drawable): ByteArray { - val bitmap = drawableToBitmap(drawable) - val stream = ByteArrayOutputStream() - bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream) - return stream.toByteArray() - } - - private fun drawableToBitmap(drawable: Drawable): Bitmap { - if (drawable is BitmapDrawable) { - return drawable.bitmap + val threadPoolExecutor = Executors.newFixedThreadPool(6) + installedApps.map { app -> + val task: Callable = Callable { + AppInfo.create(packageManager, app, withIcon) + } + threadPoolExecutor.submit(task) + }.map { future -> + future.get() + }.let { + threadPoolExecutor.shutdown() + threadPoolExecutor.awaitTermination(3, TimeUnit.SECONDS) + return it } - val bitmap = Bitmap.createBitmap( - drawable.intrinsicWidth, - drawable.intrinsicHeight, - Bitmap.Config.ARGB_8888 - ) - val canvas = Canvas(bitmap) - drawable.setBounds(0, 0, canvas.width, canvas.height) - drawable.draw(canvas) - return bitmap } - @Suppress("DEPRECATION") - private fun getVersionCode(packageInfo: PackageInfo): Long { - return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) packageInfo.versionCode.toLong() - else packageInfo.longVersionCode - } +} -} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/network/proxy/vpn/socket/CloseableConnection.kt b/android/app/src/main/kotlin/com/network/proxy/vpn/socket/CloseableConnection.kt index 1b13a5f..3da15ee 100644 --- a/android/app/src/main/kotlin/com/network/proxy/vpn/socket/CloseableConnection.kt +++ b/android/app/src/main/kotlin/com/network/proxy/vpn/socket/CloseableConnection.kt @@ -6,5 +6,5 @@ interface CloseableConnection { /** * 关闭连接 */ - fun closeConnection(session: Connection) + fun closeConnection(connection: Connection) } \ No newline at end of file diff --git a/lib/native/installed_apps.dart b/lib/native/installed_apps.dart index 14cfd5a..b422ed1 100644 --- a/lib/native/installed_apps.dart +++ b/lib/native/installed_apps.dart @@ -19,7 +19,6 @@ class AppInfo { String? name; String? packageName; String? versionName; - int? versionCode; //icon Uint8List? icon; @@ -28,7 +27,6 @@ class AppInfo { this.name, this.packageName, this.versionName, - this.versionCode, this.icon, }); @@ -36,7 +34,6 @@ class AppInfo { name = json['name']; packageName = json['packageName']; versionName = json['versionName']; - versionCode = json['versionCode']; icon = json['icon']; } @@ -45,10 +42,10 @@ class AppInfo { data['name'] = name; data['packageName'] = packageName; data['versionName'] = versionName; - data['versionCode'] = versionCode; data['icon'] = icon; return data; } + @override String toString() { return toJson().toString(); diff --git a/lib/network/channel.dart b/lib/network/channel.dart index 445b6ce..37b2be6 100644 --- a/lib/network/channel.dart +++ b/lib/network/channel.dart @@ -28,6 +28,7 @@ import 'package:network_proxy/network/util/attribute_keys.dart'; import 'package:network_proxy/network/util/byte_buf.dart'; import 'package:network_proxy/network/util/logger.dart'; import 'package:network_proxy/network/util/process_info.dart'; +import 'package:network_proxy/network/util/socket_address.dart'; import 'package:network_proxy/utils/lang.dart'; import 'handler.dart'; @@ -64,8 +65,7 @@ class Channel { bool isOpen = true; //此通道连接到的远程地址 - final InternetAddress remoteAddress; - final int remotePort; + final InetSocketAddress remoteSocketAddress; //是否写入中 bool isWriting = false; @@ -74,8 +74,7 @@ class Channel { Channel(this._socket) : _id = DateTime.now().millisecondsSinceEpoch + Random().nextInt(999999), - remoteAddress = _socket.remoteAddress, - remotePort = _socket.remotePort; + remoteSocketAddress = InetSocketAddress(_socket.remoteAddress, _socket.remotePort); ///返回此channel的全局唯一标识符。 String get id => _id.toRadixString(36); @@ -153,7 +152,7 @@ class Channel { @override String toString() { - return 'Channel($id ${remoteAddress.host}:$remotePort)'; + return 'Channel($id $remoteSocketAddress'; } } @@ -332,15 +331,18 @@ class ChannelPipeline extends ChannelHandler { if (data.method != HttpMethod.connect) { try { - data.processInfo ??= await ProcessInfoUtils.getProcessByPort(channel.remotePort, data.remoteDomain()!); - } catch (ignore) {/*ignore*/} + data.processInfo ??= + await ProcessInfoUtils.getProcessByPort(channel.remoteSocketAddress, data.remoteDomain()!); + } catch (ignore) { + /*ignore*/ + } } } if (data is HttpResponse) { data.requestId = channelContext.currentRequest?.requestId ?? data.requestId; data.packageSize = length; - data.remoteAddress = '${channel.remoteAddress.host}:${channel.remotePort}'; + data.remoteAddress = '${channel.remoteSocketAddress.host}:${channel.remoteSocketAddress.port}'; data.request ??= channelContext.currentRequest; channelContext.currentRequest?.response = data; } diff --git a/lib/network/proxy_helper.dart b/lib/network/proxy_helper.dart index 752f30a..c3ca44e 100644 --- a/lib/network/proxy_helper.dart +++ b/lib/network/proxy_helper.dart @@ -66,7 +66,7 @@ class ProxyHelper { static exceptionHandler( ChannelContext channelContext, Channel channel, EventListener? listener, HttpRequest? request, error) { HostAndPort? hostAndPort = channelContext.host; - hostAndPort ??= HostAndPort.host(scheme: HostAndPort.httpScheme, channel.remoteAddress.host, channel.remotePort); + hostAndPort ??= HostAndPort.host(scheme: HostAndPort.httpScheme, channel.remoteSocketAddress.host, channel.remoteSocketAddress.port); String message = error.toString(); HttpStatus status = HttpStatus(-1, message); if (error is HandshakeException) { diff --git a/lib/network/util/socket_address.dart b/lib/network/util/socket_address.dart new file mode 100644 index 0000000..a5b4ca5 --- /dev/null +++ b/lib/network/util/socket_address.dart @@ -0,0 +1,15 @@ +import 'dart:io'; + +class InetSocketAddress { + final InternetAddress address; + final int port; + + InetSocketAddress(this.address, this.port); + + String get host => address.host; + + @override + String toString() { + return "InetSocketAddress($address:$port)"; + } +} diff --git a/lib/storage/histories.dart b/lib/storage/histories.dart index 31e6a10..f718d7f 100644 --- a/lib/storage/histories.dart +++ b/lib/storage/histories.dart @@ -229,6 +229,7 @@ class HistoryTask extends ListenerListEvent { open = await open?.truncate(0); await open?.setPosition(0); history?.requestLength = 0; + history?.requests = null; writeList.clear(); writeList.addAll(sourceList.source); locked = false;