Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
M
Magisk
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Magisk
Commits
31f88e0f
Commit
31f88e0f
authored
Mar 25, 2022
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update UI for sharedUID support
parent
9f1740cc
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
126 additions
and
121 deletions
+126
-121
PolicyDao.kt
...main/java/com/topjohnwu/magisk/core/magiskdb/PolicyDao.kt
+15
-14
SuPolicy.kt
.../main/java/com/topjohnwu/magisk/core/model/su/SuPolicy.kt
+7
-60
SuRequestHandler.kt
...ain/java/com/topjohnwu/magisk/core/su/SuRequestHandler.kt
+7
-5
PolicyRvItem.kt
...in/java/com/topjohnwu/magisk/ui/superuser/PolicyRvItem.kt
+21
-9
SuperuserViewModel.kt
...a/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt
+62
-23
SuRequestViewModel.kt
...a/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt
+12
-8
item_policy_md2.xml
app/src/main/res/layout/item_policy_md2.xml
+2
-2
No files found.
app/src/main/java/com/topjohnwu/magisk/core/magiskdb/PolicyDao.kt
View file @
31f88e0f
...
...
@@ -2,9 +2,7 @@ package com.topjohnwu.magisk.core.magiskdb
import
com.topjohnwu.magisk.core.Const
import
com.topjohnwu.magisk.core.model.su.SuPolicy
import
com.topjohnwu.magisk.core.model.su.createPolicy
import
com.topjohnwu.magisk.di.AppContext
import
timber.log.Timber
import
java.util.concurrent.TimeUnit
class
PolicyDao
:
MagiskDB
()
{
...
...
@@ -23,28 +21,31 @@ class PolicyDao : MagiskDB() {
suspend
fun
fetch
(
uid
:
Int
):
SuPolicy
?
{
val
query
=
"SELECT * FROM ${Table.POLICY} WHERE uid == $uid LIMIT = 1"
return
exec
(
query
)
{
it
.
toPolicyOrNull
()
}
.
firstOrNull
()
return
exec
(
query
,
::
toPolicy
)
.
firstOrNull
()
}
suspend
fun
update
(
policy
:
SuPolicy
)
{
val
query
=
"REPLACE INTO ${Table.POLICY} ${policy.toMap().toQuery()}"
val
map
=
policy
.
toMap
()
// Put in package_name for old database
map
[
"package_name"
]
=
AppContext
.
packageManager
.
getNameForUid
(
policy
.
uid
)
!!
val
query
=
"REPLACE INTO ${Table.POLICY} ${map.toQuery()}"
exec
(
query
)
}
suspend
fun
fetchAll
():
List
<
SuPolicy
>
{
val
query
=
"SELECT * FROM ${Table.POLICY} WHERE uid/100000 == ${Const.USER_ID}"
return
exec
(
query
)
{
it
.
toPolicyOrNull
()
}
.
filterNotNull
()
return
exec
(
query
,
::
toPolicy
)
.
filterNotNull
()
}
private
suspend
fun
Map
<
String
,
String
>.
toPolicyOrNull
(
):
SuPolicy
?
{
try
{
return
AppContext
.
packageManager
.
createPolicy
(
this
)
}
catch
(
e
:
Exception
)
{
Timber
.
w
(
e
)
val
uid
=
get
(
"uid"
)
?:
return
null
delete
(
uid
.
toInt
())
return
null
}
private
fun
toPolicy
(
map
:
Map
<
String
,
String
>
):
SuPolicy
?
{
val
uid
=
map
[
"uid"
]
?.
toInt
()
?:
return
null
val
policy
=
SuPolicy
(
uid
)
map
[
"policy"
]
?.
toInt
()
?.
let
{
policy
.
policy
=
it
}
map
[
"until"
]
?.
toLong
()
?.
let
{
policy
.
until
=
it
}
map
[
"logging"
]
?.
toInt
()
?.
let
{
policy
.
logging
=
it
!=
0
}
map
[
"notification"
]
?.
toInt
()
?.
let
{
policy
.
notification
=
it
!=
0
}
return
policy
}
}
app/src/main/java/com/topjohnwu/magisk/core/model/su/SuPolicy.kt
View file @
31f88e0f
package
com.topjohnwu.magisk.core.model.su
import
android.content.pm.PackageInfo
import
android.content.pm.PackageManager
import
android.graphics.drawable.Drawable
import
com.topjohnwu.magisk.ktx.getLabel
import
com.topjohnwu.magisk.ktx.getPackageInfo
class
SuPolicy
(
val
uid
:
Int
,
val
packageName
:
String
,
val
appName
:
String
,
val
icon
:
Drawable
,
var
policy
:
Int
=
INTERACTIVE
,
var
until
:
Long
=
-
1L
,
var
logging
:
Boolean
=
true
,
var
notification
:
Boolean
=
true
)
{
class
SuPolicy
(
val
uid
:
Int
)
{
companion
object
{
const
val
INTERACTIVE
=
0
const
val
DENY
=
1
const
val
ALLOW
=
2
}
fun
toMap
()
=
mapOf
(
var
policy
:
Int
=
INTERACTIVE
var
until
:
Long
=
-
1L
var
logging
:
Boolean
=
true
var
notification
:
Boolean
=
true
fun
toMap
():
MutableMap
<
String
,
Any
>
=
mutableMapOf
(
"uid"
to
uid
,
"package_name"
to
packageName
,
"policy"
to
policy
,
"until"
to
until
,
"logging"
to
logging
,
"notification"
to
notification
)
}
fun
PackageManager
.
createPolicy
(
info
:
PackageInfo
):
SuPolicy
{
val
appInfo
=
info
.
applicationInfo
val
prefix
=
if
(
info
.
sharedUserId
==
null
)
""
else
"[SharedUID] "
return
SuPolicy
(
uid
=
appInfo
.
uid
,
packageName
=
getNameForUid
(
appInfo
.
uid
)
!!
,
appName
=
"$prefix${appInfo.getLabel(this)}"
,
icon
=
appInfo
.
loadIcon
(
this
),
)
}
@Throws
(
PackageManager
.
NameNotFoundException
::
class
)
fun
PackageManager
.
createPolicy
(
uid
:
Int
):
SuPolicy
{
val
info
=
getPackageInfo
(
uid
,
-
1
)
return
if
(
info
==
null
)
{
// We can assert getNameForUid does not return null because
// getPackageInfo will already throw if UID does not exist
val
name
=
getNameForUid
(
uid
)
!!
SuPolicy
(
uid
=
uid
,
packageName
=
name
,
appName
=
"[SharedUID] $name"
,
icon
=
defaultActivityIcon
,
)
}
else
{
createPolicy
(
info
)
}
}
@Throws
(
PackageManager
.
NameNotFoundException
::
class
)
fun
PackageManager
.
createPolicy
(
map
:
Map
<
String
,
String
>):
SuPolicy
{
val
uid
=
map
[
"uid"
]
?.
toIntOrNull
()
?:
throw
IllegalArgumentException
()
val
policy
=
createPolicy
(
uid
)
map
[
"policy"
]
?.
toInt
()
?.
let
{
policy
.
policy
=
it
}
map
[
"until"
]
?.
toLong
()
?.
let
{
policy
.
until
=
it
}
map
[
"logging"
]
?.
toInt
()
?.
let
{
policy
.
logging
=
it
!=
0
}
map
[
"notification"
]
?.
toInt
()
?.
let
{
policy
.
notification
=
it
!=
0
}
return
policy
}
app/src/main/java/com/topjohnwu/magisk/core/su/SuRequestHandler.kt
View file @
31f88e0f
package
com.topjohnwu.magisk.core.su
import
android.content.Intent
import
android.content.pm.PackageInfo
import
android.content.pm.PackageManager
import
com.topjohnwu.magisk.BuildConfig
import
com.topjohnwu.magisk.core.Config
import
com.topjohnwu.magisk.core.magiskdb.PolicyDao
import
com.topjohnwu.magisk.core.model.su.SuPolicy
import
com.topjohnwu.magisk.core.model.su.createPolicy
import
com.topjohnwu.magisk.ktx.getPackageInfo
import
com.topjohnwu.superuser.Shell
import
kotlinx.coroutines.Dispatchers
...
...
@@ -19,13 +19,15 @@ import java.io.IOException
import
java.util.concurrent.TimeUnit
class
SuRequestHandler
(
private
val
pm
:
PackageManager
,
val
pm
:
PackageManager
,
private
val
policyDB
:
PolicyDao
)
:
Closeable
{
private
lateinit
var
output
:
DataOutputStream
lateinit
var
policy
:
SuPolicy
private
set
lateinit
var
pkgInfo
:
PackageInfo
private
set
// Return true to indicate undetermined policy, require user interaction
suspend
fun
start
(
intent
:
Intent
):
Boolean
{
...
...
@@ -33,7 +35,7 @@ class SuRequestHandler(
return
false
// Never allow com.topjohnwu.magisk (could be malware)
if
(
p
olicy
.
packageName
==
BuildConfig
.
APPLICATION_ID
)
{
if
(
p
kgInfo
.
packageName
==
BuildConfig
.
APPLICATION_ID
)
{
Shell
.
cmd
(
"(pm uninstall ${BuildConfig.APPLICATION_ID})& >/dev/null 2>&1"
).
exec
()
return
false
}
...
...
@@ -64,9 +66,9 @@ class SuRequestHandler(
val
fifo
=
intent
.
getStringExtra
(
"fifo"
)
?:
throw
SuRequestError
()
val
uid
=
intent
.
getIntExtra
(
"uid"
,
-
1
).
also
{
if
(
it
<
0
)
throw
SuRequestError
()
}
val
pid
=
intent
.
getIntExtra
(
"pid"
,
-
1
)
val
i
nfo
=
pm
.
getPackageInfo
(
uid
,
pid
)
?:
throw
SuRequestError
()
pkgI
nfo
=
pm
.
getPackageInfo
(
uid
,
pid
)
?:
throw
SuRequestError
()
output
=
DataOutputStream
(
FileOutputStream
(
fifo
).
buffered
())
policy
=
pm
.
createPolicy
(
info
)
policy
=
SuPolicy
(
uid
)
true
}
catch
(
e
:
Exception
)
{
when
(
e
)
{
...
...
app/src/main/java/com/topjohnwu/magisk/ui/superuser/PolicyRvItem.kt
View file @
31f88e0f
...
...
@@ -10,37 +10,49 @@ import com.topjohnwu.magisk.databinding.RvContainer
import
com.topjohnwu.magisk.databinding.set
class
PolicyRvItem
(
private
val
viewModel
:
SuperuserViewModel
,
override
val
item
:
SuPolicy
,
val
packageName
:
String
,
private
val
isSharedUid
:
Boolean
,
val
icon
:
Drawable
,
val
viewModel
:
SuperuserViewModel
val
appName
:
String
)
:
ObservableDiffRvItem
<
PolicyRvItem
>(),
RvContainer
<
SuPolicy
>
{
override
val
layoutRes
=
R
.
layout
.
item_policy_md2
val
title
get
()
=
if
(
isSharedUid
)
"[SharedUID] $appName"
else
appName
private
inline
fun
<
reified
T
>
setImpl
(
new
:
T
,
old
:
T
,
setter
:
(
T
)
->
Unit
)
{
if
(
old
!=
new
)
{
setter
(
new
)
}
}
@get
:
Bindable
var
isExpanded
=
false
set
(
value
)
=
set
(
value
,
field
,
{
field
=
it
},
BR
.
expanded
)
// This property binds with the UI state
@get
:
Bindable
var
isEnabled
get
()
=
item
.
policy
==
SuPolicy
.
ALLOW
set
(
value
)
{
if
(
value
!=
isEnabled
)
set
(
value
)
=
setImpl
(
value
,
isEnabled
)
{
viewModel
.
togglePolicy
(
this
,
value
)
}
@get
:
Bindable
var
shouldNotify
get
()
=
item
.
notification
set
(
value
)
=
set
(
value
,
shouldNotify
,
{
item
.
notification
=
it
},
BR
.
shouldNotify
)
{
viewModel
.
updatePolicy
(
item
,
isLogging
=
false
)
private
set
(
value
)
=
setImpl
(
value
,
shouldNotify
)
{
item
.
notification
=
it
viewModel
.
updateNotify
(
this
)
}
@get
:
Bindable
var
shouldLog
get
()
=
item
.
logging
set
(
value
)
=
set
(
value
,
shouldLog
,
{
item
.
logging
=
it
},
BR
.
shouldLog
)
{
viewModel
.
updatePolicy
(
item
,
isLogging
=
true
)
private
set
(
value
)
=
setImpl
(
value
,
shouldLog
)
{
item
.
logging
=
it
viewModel
.
updateLogging
(
this
)
}
fun
toggleExpand
()
{
...
...
app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt
View file @
31f88e0f
package
com.topjohnwu.magisk.ui.superuser
import
android.annotation.SuppressLint
import
android.content.pm.PackageManager
import
android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES
import
androidx.databinding.ObservableArrayList
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.BR
...
...
@@ -12,9 +15,11 @@ import com.topjohnwu.magisk.core.utils.currentLocale
import
com.topjohnwu.magisk.databinding.AnyDiffRvItem
import
com.topjohnwu.magisk.databinding.diffListOf
import
com.topjohnwu.magisk.databinding.itemBindingOf
import
com.topjohnwu.magisk.di.AppContext
import
com.topjohnwu.magisk.events.SnackbarEvent
import
com.topjohnwu.magisk.events.dialog.BiometricEvent
import
com.topjohnwu.magisk.events.dialog.SuperuserRevokeDialog
import
com.topjohnwu.magisk.ktx.getLabel
import
com.topjohnwu.magisk.utils.Utils
import
com.topjohnwu.magisk.utils.asText
import
com.topjohnwu.magisk.view.TextItem
...
...
@@ -41,6 +46,7 @@ class SuperuserViewModel(
// ---
@SuppressLint
(
"InlinedApi"
)
override
fun
refresh
()
=
viewModelScope
.
launch
{
if
(!
Utils
.
showSuperUser
())
{
state
=
State
.
LOADING_FAILED
...
...
@@ -49,11 +55,28 @@ class SuperuserViewModel(
state
=
State
.
LOADING
val
(
policies
,
diff
)
=
withContext
(
Dispatchers
.
IO
)
{
db
.
deleteOutdated
()
val
policies
=
db
.
fetchAll
().
map
{
PolicyRvItem
(
it
,
it
.
icon
,
this
@SuperuserViewModel
)
}.
sortedWith
(
compareBy
(
{
it
.
item
.
appName
.
lowercase
(
currentLocale
)
},
{
it
.
item
.
packageName
}
val
policies
=
ArrayList
<
PolicyRvItem
>()
val
pm
=
AppContext
.
packageManager
for
(
policy
in
db
.
fetchAll
())
{
val
pkgs
=
pm
.
getPackagesForUid
(
policy
.
uid
)
?:
continue
policies
.
addAll
(
pkgs
.
mapNotNull
{
pkg
->
try
{
val
info
=
pm
.
getPackageInfo
(
pkg
,
MATCH_UNINSTALLED_PACKAGES
)
PolicyRvItem
(
this
@SuperuserViewModel
,
policy
,
info
.
packageName
,
info
.
sharedUserId
!=
null
,
info
.
applicationInfo
.
loadIcon
(
pm
),
info
.
applicationInfo
.
getLabel
(
pm
)
)
}
catch
(
e
:
PackageManager
.
NameNotFoundException
)
{
null
}
})
}
policies
.
sortWith
(
compareBy
(
{
it
.
appName
.
lowercase
(
currentLocale
)
},
{
it
.
packageName
}
))
policies
to
itemsPolicies
.
calculateDiff
(
policies
)
}
...
...
@@ -82,40 +105,56 @@ class SuperuserViewModel(
}.
publish
()
}
else
{
SuperuserRevokeDialog
{
appName
=
item
.
item
.
appNam
e
appName
=
item
.
titl
e
onSuccess
{
updateState
()
}
}.
publish
()
}
}
//---
fun
updatePolicy
(
policy
:
SuPolicy
,
isLogging
:
Boolean
)
=
viewModelScope
.
launch
{
db
.
update
(
policy
)
fun
updateNotify
(
item
:
PolicyRvItem
)
{
viewModelScope
.
launch
{
db
.
update
(
item
.
item
)
val
res
=
when
{
isLogging
->
when
{
policy
.
logging
->
R
.
string
.
su_snack_log_on
item
.
item
.
logging
->
R
.
string
.
su_snack_log_on
else
->
R
.
string
.
su_snack_log_off
}
else
->
when
{
policy
.
notification
->
R
.
string
.
su_snack_notif_on
itemsPolicies
.
forEach
{
if
(
it
.
item
.
uid
==
item
.
item
.
uid
)
{
it
.
notifyPropertyChanged
(
BR
.
shouldNotify
)
}
}
SnackbarEvent
(
res
.
asText
(
item
.
appName
)).
publish
()
}
}
fun
updateLogging
(
item
:
PolicyRvItem
)
{
viewModelScope
.
launch
{
db
.
update
(
item
.
item
)
val
res
=
when
{
item
.
item
.
notification
->
R
.
string
.
su_snack_notif_on
else
->
R
.
string
.
su_snack_notif_off
}
itemsPolicies
.
forEach
{
if
(
it
.
item
.
uid
==
item
.
item
.
uid
)
{
it
.
notifyPropertyChanged
(
BR
.
shouldLog
)
}
}
SnackbarEvent
(
res
.
asText
(
item
.
appName
)).
publish
()
}
SnackbarEvent
(
res
.
asText
(
policy
.
appName
)).
publish
()
}
fun
togglePolicy
(
item
:
PolicyRvItem
,
enable
:
Boolean
)
{
fun
updateState
()
{
val
policy
=
if
(
enable
)
SuPolicy
.
ALLOW
else
SuPolicy
.
DENY
item
.
item
.
policy
=
policy
item
.
notifyPropertyChanged
(
BR
.
enabled
)
viewModelScope
.
launch
{
val
res
=
if
(
enable
)
R
.
string
.
su_snack_grant
else
R
.
string
.
su_snack_deny
item
.
item
.
policy
=
if
(
enable
)
SuPolicy
.
ALLOW
else
SuPolicy
.
DENY
db
.
update
(
item
.
item
)
val
res
=
if
(
item
.
item
.
policy
==
SuPolicy
.
ALLOW
)
R
.
string
.
su_snack_grant
else
R
.
string
.
su_snack_deny
SnackbarEvent
(
res
.
asText
(
item
.
item
.
appName
)).
publish
()
itemsPolicies
.
forEach
{
if
(
it
.
item
.
uid
==
item
.
item
.
uid
)
{
it
.
notifyPropertyChanged
(
BR
.
enabled
)
}
}
SnackbarEvent
(
res
.
asText
(
item
.
appName
)).
publish
()
}
}
...
...
app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt
View file @
31f88e0f
...
...
@@ -21,7 +21,6 @@ import com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.arch.BaseViewModel
import
com.topjohnwu.magisk.core.Config
import
com.topjohnwu.magisk.core.magiskdb.PolicyDao
import
com.topjohnwu.magisk.core.model.su.SuPolicy
import
com.topjohnwu.magisk.core.model.su.SuPolicy.Companion.ALLOW
import
com.topjohnwu.magisk.core.model.su.SuPolicy.Companion.DENY
import
com.topjohnwu.magisk.core.su.SuRequestHandler
...
...
@@ -31,6 +30,7 @@ import com.topjohnwu.magisk.di.AppContext
import
com.topjohnwu.magisk.events.DieEvent
import
com.topjohnwu.magisk.events.ShowUIEvent
import
com.topjohnwu.magisk.events.dialog.BiometricEvent
import
com.topjohnwu.magisk.ktx.getLabel
import
com.topjohnwu.magisk.utils.TextHolder
import
com.topjohnwu.magisk.utils.Utils
import
kotlinx.coroutines.launch
...
...
@@ -100,17 +100,21 @@ class SuRequestViewModel(
fun
handleRequest
(
intent
:
Intent
)
{
viewModelScope
.
launch
{
if
(
handler
.
start
(
intent
))
showDialog
(
handler
.
policy
)
showDialog
()
else
DieEvent
().
publish
()
}
}
private
fun
showDialog
(
policy
:
SuPolicy
)
{
icon
=
policy
.
icon
title
=
policy
.
appName
packageName
=
policy
.
packageName
selectedItemPosition
=
timeoutPrefs
.
getInt
(
policy
.
packageName
,
0
)
private
fun
showDialog
()
{
val
pm
=
handler
.
pm
val
info
=
handler
.
pkgInfo
val
prefix
=
if
(
info
.
sharedUserId
==
null
)
""
else
"[SharedUID] "
icon
=
info
.
applicationInfo
.
loadIcon
(
pm
)
title
=
"$prefix${info.applicationInfo.getLabel(pm)}"
packageName
=
info
.
packageName
selectedItemPosition
=
timeoutPrefs
.
getInt
(
packageName
,
0
)
// Set timer
val
millis
=
SECONDS
.
toMillis
(
Config
.
suDefaultTimeout
.
toLong
())
...
...
@@ -124,7 +128,7 @@ class SuRequestViewModel(
timer
?.
cancel
()
val
pos
=
selectedItemPosition
timeoutPrefs
.
edit
().
putInt
(
handler
.
p
olicy
.
packageName
,
pos
).
apply
()
timeoutPrefs
.
edit
().
putInt
(
handler
.
p
kgInfo
.
packageName
,
pos
).
apply
()
viewModelScope
.
launch
{
handler
.
respond
(
action
,
Config
.
Value
.
TIMEOUT_LIST
[
pos
])
...
...
app/src/main/res/layout/item_policy_md2.xml
View file @
31f88e0f
...
...
@@ -54,7 +54,7 @@
android:ellipsize=
"middle"
android:gravity=
"start"
android:maxLines=
"2"
android:text=
"@{item.
item.appNam
e}"
android:text=
"@{item.
titl
e}"
android:textAppearance=
"@style/AppearanceFoundation.Body"
android:textIsSelectable=
"false"
android:textStyle=
"bold"
...
...
@@ -71,7 +71,7 @@
android:ellipsize=
"middle"
android:gravity=
"start"
android:maxLines=
"2"
android:text=
"@{item.
item.
packageName}"
android:text=
"@{item.packageName}"
android:textAppearance=
"@style/AppearanceFoundation.Caption.Variant"
android:textColor=
"@android:color/tertiary_text_dark"
android:textIsSelectable=
"false"
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment