From ad831aa1f43fbd89c067ccb798dfcfe8e7bb4fc4 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 6 Dec 2015 21:20:30 +0800 Subject: [PATCH] Persist package names in individual/proxied --- .../com/github/shadowsocks/AppManager.scala | 39 ++++++++++--------- .../com/github/shadowsocks/Shadowsocks.scala | 15 ++++--- .../shadowsocks/ShadowsocksNatService.scala | 18 +++++---- .../shadowsocks/ShadowsocksVpnService.scala | 2 +- .../shadowsocks/database/DBHelper.scala | 26 ++++++++++--- 5 files changed, 62 insertions(+), 38 deletions(-) diff --git a/src/main/scala/com/github/shadowsocks/AppManager.scala b/src/main/scala/com/github/shadowsocks/AppManager.scala index 81c17db3..0d9dbab8 100644 --- a/src/main/scala/com/github/shadowsocks/AppManager.scala +++ b/src/main/scala/com/github/shadowsocks/AppManager.scala @@ -61,13 +61,13 @@ import scala.collection.mutable import scala.language.implicitConversions object AppManager { - case class ProxiedApp(uid: Int, name: String, packageName: String, icon: Drawable) + case class ProxiedApp(name: String, packageName: String, icon: Drawable) private case class ListEntry(switch: Switch, text: TextView, icon: ImageView) var cachedApps: Array[ProxiedApp] = _ private def getApps(pm: PackageManager) = { - if (cachedApps == null) cachedApps = pm.getInstalledApplications(0).filter(_.uid >= 10000) - .map(a => new ProxiedApp(a.uid, pm.getApplicationLabel(a).toString, a.packageName, a.loadIcon(pm))).toArray + if (cachedApps == null) cachedApps = pm.getInstalledApplications(0) + .map(a => new ProxiedApp(pm.getApplicationLabel(a).toString, a.packageName, a.loadIcon(pm))).toArray cachedApps } } @@ -77,7 +77,7 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC import AppManager._ private var apps: Array[ProxiedApp] = _ - private var proxiedApps: mutable.HashSet[Int] = _ + private var proxiedApps: mutable.HashSet[String] = _ private var toolbar: Toolbar = _ private var appListView: ListView = _ private var loadingView: View = _ @@ -87,10 +87,10 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC def loadApps() { appsLoading = true - proxiedApps = ShadowsocksApplication.settings.getString(Key.proxied, "").split('|').map(_.toInt).to[mutable.HashSet] + proxiedApps = ShadowsocksApplication.settings.getString(Key.proxied, "").split('\n').to[mutable.HashSet] apps = getApps(getPackageManager).sortWith((a, b) => { - val aProxied = proxiedApps.contains(a.uid) - if (aProxied ^ proxiedApps.contains(b.uid)) aProxied else a.name < b.name + val aProxied = proxiedApps.contains(a.packageName) + if (aProxied ^ proxiedApps.contains(b.packageName)) aProxied else a.name < b.name }) adapter = new ArrayAdapter[ProxiedApp](this, R.layout.layout_apps_item, R.id.itemtext, apps) { override def getView(position: Int, view: View, parent: ViewGroup): View = { @@ -114,19 +114,19 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC entry.icon.setImageDrawable(app.icon) val switch = entry.switch switch.setTag(app) - switch.setChecked(proxiedApps.contains(app.uid)) + switch.setChecked(proxiedApps.contains(app.packageName)) entry.text.setTag(switch) convertView } } } - private def setProxied(uid: Int, proxied: Boolean) = if (proxied) proxiedApps.add(uid) else proxiedApps.remove(uid) + private def setProxied(pn: String, proxied: Boolean) = if (proxied) proxiedApps.add(pn) else proxiedApps.remove(pn) /** Called an application is check/unchecked */ def onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) { val app: ProxiedApp = buttonView.getTag.asInstanceOf[ProxiedApp] - if (app != null) setProxied(app.uid, isChecked) + if (app != null) setProxied(app.packageName, isChecked) saveAppSettings(this) } @@ -134,8 +134,8 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC val switch = v.getTag.asInstanceOf[ListEntry].switch val app: ProxiedApp = switch.getTag.asInstanceOf[ProxiedApp] if (app != null) { - val proxied = !proxiedApps.contains(app.uid) - setProxied(app.uid, proxied) + val proxied = !proxiedApps.contains(app.packageName) + setProxied(app.packageName, proxied) switch.setChecked(proxied) } saveAppSettings(this) @@ -156,7 +156,7 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC case R.id.action_export => val bypass = prefs.getBoolean(Key.isBypassApps, false) val proxiedAppString = prefs.getString(Key.proxied, "") - val clip = ClipData.newPlainText(Key.proxied, bypass + " " + proxiedAppString) + val clip = ClipData.newPlainText(Key.proxied, bypass + "\n" + proxiedAppString) clipboard.setPrimaryClip(clip) Toast.makeText(this, R.string.action_export_msg, Toast.LENGTH_SHORT).show() return true @@ -169,11 +169,12 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC if (proxiedAppSequence != null) { val proxiedAppString = proxiedAppSequence.toString if (!proxiedAppString.isEmpty) { - val array = proxiedAppString.split(" ") - val bypass = array(0).toBoolean - val apps = if (array.size > 1) array(1) else "" - prefs.edit.putBoolean(Key.isBypassApps, bypass).apply() - prefs.edit.putString(Key.proxied, apps).apply() + val editor = prefs.edit + val i = proxiedAppString.indexOf('\n') + if (i < 0) + editor.putBoolean(Key.isBypassApps, proxiedAppString.toBoolean).putString(Key.proxied, "").apply() + else editor.putBoolean(Key.isBypassApps, proxiedAppString.substring(0, i).toBoolean) + .putString(Key.proxied, proxiedAppString.substring(i + 1)).apply() Toast.makeText(this, R.string.action_import_msg, Toast.LENGTH_SHORT).show() // Restart activity appListView.setVisibility(View.GONE) @@ -258,7 +259,7 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC } def saveAppSettings(context: Context) { - if (!appsLoading) ShadowsocksApplication.settings.edit.putString(Key.proxied, proxiedApps.mkString("|")).apply + if (!appsLoading) ShadowsocksApplication.settings.edit.putString(Key.proxied, proxiedApps.mkString("\n")).apply } var handler: Handler = null diff --git a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala index 881c999e..a301deed 100644 --- a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala +++ b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala @@ -218,18 +218,21 @@ class Shadowsocks ShadowsocksApplication.settings.edit.putBoolean(ShadowsocksApplication.getVersionName, true).apply() try { // Workaround that convert port(String) to port(Int) - val oldLocalPort = ShadowsocksApplication.settings.getString("port", "-1") - val oldRemotePort = ShadowsocksApplication.settings.getString("remotePort", "-1") + val oldLocalPort = ShadowsocksApplication.settings.getString(Key.localPort, "") + val oldRemotePort = ShadowsocksApplication.settings.getString(Key.remotePort, "") - if (oldLocalPort != "-1") { - ShadowsocksApplication.settings.edit.putInt(Key.localPort, oldLocalPort.toInt).commit() + if (oldLocalPort != "") { + ShadowsocksApplication.settings.edit.putInt(Key.localPort, oldLocalPort.toInt).apply() } - if (oldRemotePort != "-1") { - ShadowsocksApplication.settings.edit.putInt(Key.remotePort, oldRemotePort.toInt).commit() + if (oldRemotePort != "") { + ShadowsocksApplication.settings.edit.putInt(Key.remotePort, oldRemotePort.toInt).apply() } } catch { case ex: Exception => // Ignore } + val oldProxiedApps = ShadowsocksApplication.settings.getString(Key.proxied, "") + if (oldProxiedApps.contains('|')) ShadowsocksApplication.settings.edit + .putString(Key.proxied, DBHelper.updateProxiedApps(this, oldProxiedApps)).apply() recovery() } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala index 26374ffc..47f91d78 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala @@ -374,12 +374,16 @@ class ShadowsocksNatService extends BaseService { http_sb.append(Utils.getIptables + CMD_IPTABLES_DNAT_ADD_SOCKS) } if (config.isProxyApps) { - for (uid <- config.proxiedAppString.split('|').distinct) { - if (!config.isBypassApps) { - http_sb.append((Utils.getIptables + CMD_IPTABLES_DNAT_ADD_SOCKS).replace("-t nat", "-t nat -m owner --uid-owner " + uid)) - } else { - init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "-m owner --uid-owner " + uid)) - } + val uidMap = getPackageManager.getInstalledApplications(0).map(ai => ai.packageName -> ai.uid).toMap + for (pn <- config.proxiedAppString.split('\n')) uidMap.get(pn) match { + case Some(uid) => + if (!config.isBypassApps) { + http_sb.append((Utils.getIptables + CMD_IPTABLES_DNAT_ADD_SOCKS) + .replace("-t nat", "-t nat -m owner --uid-owner " + uid)) + } else { + init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "-m owner --uid-owner " + uid)) + } + case _ => // probably removed package, ignore } } Console.runRootCommand((init_sb ++ http_sb).toArray) @@ -392,7 +396,7 @@ class ShadowsocksNatService extends BaseService { changeState(State.STOPPED, getString(R.string.nat_no_root)) return } - + super.startRunner(config) // register close receiver diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala index ac832422..849af214 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala @@ -388,7 +388,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { if (Utils.isLollipopOrAbove) { if (config.isProxyApps) { - for (pkg <- config.proxiedAppString.split('|').distinct) { + for (pkg <- config.proxiedAppString.split('\n')) { if (!config.isBypassApps) { builder.addAllowedApplication(pkg) } else { diff --git a/src/main/scala/com/github/shadowsocks/database/DBHelper.scala b/src/main/scala/com/github/shadowsocks/database/DBHelper.scala index ae685307..c5c55df1 100644 --- a/src/main/scala/com/github/shadowsocks/database/DBHelper.scala +++ b/src/main/scala/com/github/shadowsocks/database/DBHelper.scala @@ -40,18 +40,30 @@ package com.github.shadowsocks.database import android.content.Context +import android.content.pm.ApplicationInfo import android.database.sqlite.SQLiteDatabase import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper import com.j256.ormlite.dao.Dao import com.j256.ormlite.support.ConnectionSource import com.j256.ormlite.table.TableUtils +import scala.collection.JavaConverters._ +import scala.collection.mutable + object DBHelper { - val PROFILE = "profile.db" + final val PROFILE = "profile.db" + private var apps: mutable.Buffer[ApplicationInfo] = _ + + def updateProxiedApps(context: Context, old: String) = { + synchronized(if (apps == null) apps = context.getPackageManager.getInstalledApplications(0).asScala) + val uidSet = old.split('|').map(_.toInt).toSet + apps.filter(ai => uidSet.contains(ai.uid)).map(_.packageName).mkString("\n") + } } class DBHelper(val context: Context) - extends OrmLiteSqliteOpenHelper(context, DBHelper.PROFILE, null, 13) { + extends OrmLiteSqliteOpenHelper(context, DBHelper.PROFILE, null, 14) { + import DBHelper._ lazy val profileDao: Dao[Profile, Int] = getDao(classOf[Profile]) @@ -91,13 +103,17 @@ class DBHelper(val context: Context) " ipv6, individual FROM `tmp`;") profileDao.executeRawNoArgs("DROP TABLE `tmp`;") profileDao.executeRawNoArgs("COMMIT;") - return - } - if (oldVersion < 13) { + } else if (oldVersion < 13) { profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN tx LONG;") profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN rx LONG;") profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN date VARCHAR;") } + if (oldVersion < 14) { + for (profile <- profileDao.queryForAll.asScala) { + profile.individual = updateProxiedApps(context, profile.individual) + profileDao.update(profile) + } + } } } }