Commit ad831aa1 authored by Mygod's avatar Mygod

Persist package names in individual/proxied

parent 93084f0e
...@@ -61,13 +61,13 @@ import scala.collection.mutable ...@@ -61,13 +61,13 @@ import scala.collection.mutable
import scala.language.implicitConversions import scala.language.implicitConversions
object AppManager { 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) private case class ListEntry(switch: Switch, text: TextView, icon: ImageView)
var cachedApps: Array[ProxiedApp] = _ var cachedApps: Array[ProxiedApp] = _
private def getApps(pm: PackageManager) = { private def getApps(pm: PackageManager) = {
if (cachedApps == null) cachedApps = pm.getInstalledApplications(0).filter(_.uid >= 10000) if (cachedApps == null) cachedApps = pm.getInstalledApplications(0)
.map(a => new ProxiedApp(a.uid, pm.getApplicationLabel(a).toString, a.packageName, a.loadIcon(pm))).toArray .map(a => new ProxiedApp(pm.getApplicationLabel(a).toString, a.packageName, a.loadIcon(pm))).toArray
cachedApps cachedApps
} }
} }
...@@ -77,7 +77,7 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC ...@@ -77,7 +77,7 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
import AppManager._ import AppManager._
private var apps: Array[ProxiedApp] = _ private var apps: Array[ProxiedApp] = _
private var proxiedApps: mutable.HashSet[Int] = _ private var proxiedApps: mutable.HashSet[String] = _
private var toolbar: Toolbar = _ private var toolbar: Toolbar = _
private var appListView: ListView = _ private var appListView: ListView = _
private var loadingView: View = _ private var loadingView: View = _
...@@ -87,10 +87,10 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC ...@@ -87,10 +87,10 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
def loadApps() { def loadApps() {
appsLoading = true 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) => { apps = getApps(getPackageManager).sortWith((a, b) => {
val aProxied = proxiedApps.contains(a.uid) val aProxied = proxiedApps.contains(a.packageName)
if (aProxied ^ proxiedApps.contains(b.uid)) aProxied else a.name < b.name 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) { adapter = new ArrayAdapter[ProxiedApp](this, R.layout.layout_apps_item, R.id.itemtext, apps) {
override def getView(position: Int, view: View, parent: ViewGroup): View = { override def getView(position: Int, view: View, parent: ViewGroup): View = {
...@@ -114,19 +114,19 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC ...@@ -114,19 +114,19 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
entry.icon.setImageDrawable(app.icon) entry.icon.setImageDrawable(app.icon)
val switch = entry.switch val switch = entry.switch
switch.setTag(app) switch.setTag(app)
switch.setChecked(proxiedApps.contains(app.uid)) switch.setChecked(proxiedApps.contains(app.packageName))
entry.text.setTag(switch) entry.text.setTag(switch)
convertView 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 */ /** Called an application is check/unchecked */
def onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) { def onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
val app: ProxiedApp = buttonView.getTag.asInstanceOf[ProxiedApp] val app: ProxiedApp = buttonView.getTag.asInstanceOf[ProxiedApp]
if (app != null) setProxied(app.uid, isChecked) if (app != null) setProxied(app.packageName, isChecked)
saveAppSettings(this) saveAppSettings(this)
} }
...@@ -134,8 +134,8 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC ...@@ -134,8 +134,8 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
val switch = v.getTag.asInstanceOf[ListEntry].switch val switch = v.getTag.asInstanceOf[ListEntry].switch
val app: ProxiedApp = switch.getTag.asInstanceOf[ProxiedApp] val app: ProxiedApp = switch.getTag.asInstanceOf[ProxiedApp]
if (app != null) { if (app != null) {
val proxied = !proxiedApps.contains(app.uid) val proxied = !proxiedApps.contains(app.packageName)
setProxied(app.uid, proxied) setProxied(app.packageName, proxied)
switch.setChecked(proxied) switch.setChecked(proxied)
} }
saveAppSettings(this) saveAppSettings(this)
...@@ -156,7 +156,7 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC ...@@ -156,7 +156,7 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
case R.id.action_export => case R.id.action_export =>
val bypass = prefs.getBoolean(Key.isBypassApps, false) val bypass = prefs.getBoolean(Key.isBypassApps, false)
val proxiedAppString = prefs.getString(Key.proxied, "") 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) clipboard.setPrimaryClip(clip)
Toast.makeText(this, R.string.action_export_msg, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.action_export_msg, Toast.LENGTH_SHORT).show()
return true return true
...@@ -169,11 +169,12 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC ...@@ -169,11 +169,12 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
if (proxiedAppSequence != null) { if (proxiedAppSequence != null) {
val proxiedAppString = proxiedAppSequence.toString val proxiedAppString = proxiedAppSequence.toString
if (!proxiedAppString.isEmpty) { if (!proxiedAppString.isEmpty) {
val array = proxiedAppString.split(" ") val editor = prefs.edit
val bypass = array(0).toBoolean val i = proxiedAppString.indexOf('\n')
val apps = if (array.size > 1) array(1) else "" if (i < 0)
prefs.edit.putBoolean(Key.isBypassApps, bypass).apply() editor.putBoolean(Key.isBypassApps, proxiedAppString.toBoolean).putString(Key.proxied, "").apply()
prefs.edit.putString(Key.proxied, apps).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() Toast.makeText(this, R.string.action_import_msg, Toast.LENGTH_SHORT).show()
// Restart activity // Restart activity
appListView.setVisibility(View.GONE) appListView.setVisibility(View.GONE)
...@@ -258,7 +259,7 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC ...@@ -258,7 +259,7 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
} }
def saveAppSettings(context: Context) { 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 var handler: Handler = null
......
...@@ -218,18 +218,21 @@ class Shadowsocks ...@@ -218,18 +218,21 @@ class Shadowsocks
ShadowsocksApplication.settings.edit.putBoolean(ShadowsocksApplication.getVersionName, true).apply() ShadowsocksApplication.settings.edit.putBoolean(ShadowsocksApplication.getVersionName, true).apply()
try { try {
// Workaround that convert port(String) to port(Int) // Workaround that convert port(String) to port(Int)
val oldLocalPort = ShadowsocksApplication.settings.getString("port", "-1") val oldLocalPort = ShadowsocksApplication.settings.getString(Key.localPort, "")
val oldRemotePort = ShadowsocksApplication.settings.getString("remotePort", "-1") val oldRemotePort = ShadowsocksApplication.settings.getString(Key.remotePort, "")
if (oldLocalPort != "-1") { if (oldLocalPort != "") {
ShadowsocksApplication.settings.edit.putInt(Key.localPort, oldLocalPort.toInt).commit() ShadowsocksApplication.settings.edit.putInt(Key.localPort, oldLocalPort.toInt).apply()
} }
if (oldRemotePort != "-1") { if (oldRemotePort != "") {
ShadowsocksApplication.settings.edit.putInt(Key.remotePort, oldRemotePort.toInt).commit() ShadowsocksApplication.settings.edit.putInt(Key.remotePort, oldRemotePort.toInt).apply()
} }
} catch { } catch {
case ex: Exception => // Ignore 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() recovery()
} }
......
...@@ -374,12 +374,16 @@ class ShadowsocksNatService extends BaseService { ...@@ -374,12 +374,16 @@ class ShadowsocksNatService extends BaseService {
http_sb.append(Utils.getIptables + CMD_IPTABLES_DNAT_ADD_SOCKS) http_sb.append(Utils.getIptables + CMD_IPTABLES_DNAT_ADD_SOCKS)
} }
if (config.isProxyApps) { if (config.isProxyApps) {
for (uid <- config.proxiedAppString.split('|').distinct) { val uidMap = getPackageManager.getInstalledApplications(0).map(ai => ai.packageName -> ai.uid).toMap
if (!config.isBypassApps) { for (pn <- config.proxiedAppString.split('\n')) uidMap.get(pn) match {
http_sb.append((Utils.getIptables + CMD_IPTABLES_DNAT_ADD_SOCKS).replace("-t nat", "-t nat -m owner --uid-owner " + uid)) case Some(uid) =>
} else { if (!config.isBypassApps) {
init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "-m owner --uid-owner " + uid)) 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) Console.runRootCommand((init_sb ++ http_sb).toArray)
...@@ -392,7 +396,7 @@ class ShadowsocksNatService extends BaseService { ...@@ -392,7 +396,7 @@ class ShadowsocksNatService extends BaseService {
changeState(State.STOPPED, getString(R.string.nat_no_root)) changeState(State.STOPPED, getString(R.string.nat_no_root))
return return
} }
super.startRunner(config) super.startRunner(config)
// register close receiver // register close receiver
......
...@@ -388,7 +388,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { ...@@ -388,7 +388,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
if (Utils.isLollipopOrAbove) { if (Utils.isLollipopOrAbove) {
if (config.isProxyApps) { if (config.isProxyApps) {
for (pkg <- config.proxiedAppString.split('|').distinct) { for (pkg <- config.proxiedAppString.split('\n')) {
if (!config.isBypassApps) { if (!config.isBypassApps) {
builder.addAllowedApplication(pkg) builder.addAllowedApplication(pkg)
} else { } else {
......
...@@ -40,18 +40,30 @@ ...@@ -40,18 +40,30 @@
package com.github.shadowsocks.database package com.github.shadowsocks.database
import android.content.Context import android.content.Context
import android.content.pm.ApplicationInfo
import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase
import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper
import com.j256.ormlite.dao.Dao import com.j256.ormlite.dao.Dao
import com.j256.ormlite.support.ConnectionSource import com.j256.ormlite.support.ConnectionSource
import com.j256.ormlite.table.TableUtils import com.j256.ormlite.table.TableUtils
import scala.collection.JavaConverters._
import scala.collection.mutable
object DBHelper { 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) 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]) lazy val profileDao: Dao[Profile, Int] = getDao(classOf[Profile])
...@@ -91,13 +103,17 @@ class DBHelper(val context: Context) ...@@ -91,13 +103,17 @@ class DBHelper(val context: Context)
" ipv6, individual FROM `tmp`;") " ipv6, individual FROM `tmp`;")
profileDao.executeRawNoArgs("DROP TABLE `tmp`;") profileDao.executeRawNoArgs("DROP TABLE `tmp`;")
profileDao.executeRawNoArgs("COMMIT;") profileDao.executeRawNoArgs("COMMIT;")
return } else if (oldVersion < 13) {
}
if (oldVersion < 13) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN tx LONG;") 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 rx LONG;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN date VARCHAR;") 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)
}
}
} }
} }
} }
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