mirror of
https://github.com/SagerNet/sing-tun.git
synced 2025-04-04 04:17:39 +03:00
Add android package route support
This commit is contained in:
parent
b828f01643
commit
1407eae46b
7 changed files with 276 additions and 36 deletions
171
packages_android.go
Normal file
171
packages_android.go
Normal file
|
@ -0,0 +1,171 @@
|
|||
package tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
type packageManager struct {
|
||||
callback PackageManagerCallback
|
||||
watcher *fsnotify.Watcher
|
||||
idByPackage map[string][]uint32
|
||||
sharedByPackage map[string]uint32
|
||||
packageById map[uint32]string
|
||||
sharedById map[uint32]string
|
||||
}
|
||||
|
||||
func NewPackageManager(callback PackageManagerCallback) (PackageManager, error) {
|
||||
return &packageManager{callback: callback}, nil
|
||||
}
|
||||
|
||||
func (m *packageManager) Start() error {
|
||||
err := m.updatePackages()
|
||||
if err != nil {
|
||||
return E.Cause(err, "read packages list")
|
||||
}
|
||||
err = m.startWatcher()
|
||||
if err != nil {
|
||||
m.callback.NewError(context.Background(), E.Cause(err, "create fsnotify watcher"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *packageManager) startWatcher() error {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = watcher.Add("/data/system/packages.xml")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.watcher = watcher
|
||||
go m.loopUpdate()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *packageManager) loopUpdate() {
|
||||
for {
|
||||
select {
|
||||
case _, ok := <-m.watcher.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
err := m.updatePackages()
|
||||
if err != nil {
|
||||
m.callback.NewError(context.Background(), E.Cause(err, "update packages"))
|
||||
}
|
||||
case err, ok := <-m.watcher.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
m.callback.NewError(context.Background(), E.Cause(err, "fsnotify error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *packageManager) Close() error {
|
||||
return common.Close(common.PtrOrNil(m.watcher))
|
||||
}
|
||||
|
||||
func (m *packageManager) IDByPackage(packageName string) ([]uint32, bool) {
|
||||
id, loaded := m.idByPackage[packageName]
|
||||
return id, loaded
|
||||
}
|
||||
|
||||
func (m *packageManager) IDBySharedPackage(sharedPackage string) (uint32, bool) {
|
||||
id, loaded := m.sharedByPackage[sharedPackage]
|
||||
return id, loaded
|
||||
}
|
||||
|
||||
func (m *packageManager) PackageByID(id uint32) (string, bool) {
|
||||
packageName, loaded := m.packageById[id]
|
||||
return packageName, loaded
|
||||
}
|
||||
|
||||
func (m *packageManager) SharedPackageByID(id uint32) (string, bool) {
|
||||
sharedPackage, loaded := m.sharedById[id]
|
||||
return sharedPackage, loaded
|
||||
}
|
||||
|
||||
func (m *packageManager) updatePackages() error {
|
||||
idByPackage := make(map[string][]uint32)
|
||||
sharedByPackage := make(map[string]uint32)
|
||||
packageById := make(map[uint32]string)
|
||||
sharedById := make(map[uint32]string)
|
||||
packagesData, err := os.Open("/data/system/packages.xml")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
decoder := xml.NewDecoder(packagesData)
|
||||
var token xml.Token
|
||||
for {
|
||||
token, err = decoder.Token()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
element, isStart := token.(xml.StartElement)
|
||||
if !isStart {
|
||||
continue
|
||||
}
|
||||
|
||||
switch element.Name.Local {
|
||||
case "package":
|
||||
var name string
|
||||
var userID uint64
|
||||
for _, attr := range element.Attr {
|
||||
switch attr.Name.Local {
|
||||
case "name":
|
||||
name = attr.Value
|
||||
case "userId", "sharedUserId":
|
||||
userID, err = strconv.ParseUint(attr.Value, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if userID == 0 && name == "" {
|
||||
continue
|
||||
}
|
||||
idByPackage[name] = append(idByPackage[name], uint32(userID))
|
||||
packageById[uint32(userID)] = name
|
||||
case "shared-user":
|
||||
var name string
|
||||
var userID uint64
|
||||
for _, attr := range element.Attr {
|
||||
switch attr.Name.Local {
|
||||
case "name":
|
||||
name = attr.Value
|
||||
case "userId":
|
||||
userID, err = strconv.ParseUint(attr.Value, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
packageById[uint32(userID)] = name
|
||||
}
|
||||
}
|
||||
if userID == 0 && name == "" {
|
||||
continue
|
||||
}
|
||||
sharedByPackage[name] = uint32(userID)
|
||||
sharedById[uint32(userID)] = name
|
||||
}
|
||||
}
|
||||
m.idByPackage = idByPackage
|
||||
m.sharedByPackage = sharedByPackage
|
||||
m.packageById = packageById
|
||||
m.sharedById = sharedById
|
||||
m.callback.OnPackagesUpdated(len(packageById), len(sharedById))
|
||||
return nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue