You've already forked KernelSU
mirror of
https://github.com/tiann/KernelSU.git
synced 2025-08-27 23:46:34 +00:00
Compare commits
131 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e35180c919 | ||
|
|
2f54ceb7c4 | ||
|
|
6506ef468d | ||
|
|
da46dfbde1 | ||
|
|
09ecc2c9b5 | ||
|
|
0b2899a961 | ||
|
|
c6ed3fa27f | ||
|
|
43cd3b9cad | ||
|
|
294d6fa05e | ||
|
|
009a479c17 | ||
|
|
72ee14e6be | ||
|
|
2b01a1c395 | ||
|
|
f69793d38e | ||
|
|
eda0d6d23b | ||
|
|
abba36d786 | ||
|
|
33ea9a6f33 | ||
|
|
9a0da7270b | ||
|
|
9caf440200 | ||
|
|
c9e53cf355 | ||
|
|
432eb318ac | ||
|
|
cea0db4e0d | ||
|
|
842c0b674f | ||
|
|
36ed99d1f6 | ||
|
|
86d0f37b37 | ||
|
|
5303c9c461 | ||
|
|
67667b6df2 | ||
|
|
d9d27b4229 | ||
|
|
a2a1b19758 | ||
|
|
fb9d39d6d5 | ||
|
|
0f3d425f64 | ||
|
|
08e3580646 | ||
|
|
0fa4a4c6db | ||
|
|
d452e01a3d | ||
|
|
f7da373f8b | ||
|
|
b80cf7ba15 | ||
|
|
fe1cd4b27a | ||
|
|
925206f9c8 | ||
|
|
f24a317e2d | ||
|
|
aeaa3ce982 | ||
|
|
8c6913a9af | ||
|
|
7795232a58 | ||
|
|
057330c68f | ||
|
|
91c80279bd | ||
|
|
5715df0b10 | ||
|
|
9f0c540fba | ||
|
|
f9d19a957a | ||
|
|
76b1165572 | ||
|
|
80c85b3bb9 | ||
|
|
900652a82b | ||
|
|
6cbef7d987 | ||
|
|
01d66834f0 | ||
|
|
a40eae9b8c | ||
|
|
23b46bde2b | ||
|
|
b38ada30e6 | ||
|
|
3452841752 | ||
|
|
3b9cab3432 | ||
|
|
2f6df20085 | ||
|
|
609ea40d1c | ||
|
|
515a309831 | ||
|
|
02c2228ab7 | ||
|
|
ca246ba67d | ||
|
|
f9008b67c4 | ||
|
|
e228197906 | ||
|
|
d98e324618 | ||
|
|
1880fdfda9 | ||
|
|
d86b524558 | ||
|
|
70fa38a589 | ||
|
|
ea93140b8a | ||
|
|
45c229dc9f | ||
|
|
2b5ac95165 | ||
|
|
38c65e7e4b | ||
|
|
f40eaf4499 | ||
|
|
3a667ef389 | ||
|
|
f35e03d816 | ||
|
|
2bfd4d71d8 | ||
|
|
2bc84014c2 | ||
|
|
a3c72c22c1 | ||
|
|
ddf2b7e9ff | ||
|
|
04d1bee7b4 | ||
|
|
0c9ecf3abc | ||
|
|
c8b4798b61 | ||
|
|
550c8ae45d | ||
|
|
0b4f4683b3 | ||
|
|
5504b0dd29 | ||
|
|
f0dcddd391 | ||
|
|
75be1e1bd6 | ||
|
|
c569d803c5 | ||
|
|
48cd6e112b | ||
|
|
d41354e1d7 | ||
|
|
9a5e36c0a4 | ||
|
|
f963e40a5f | ||
|
|
f1e2402316 | ||
|
|
31bb8c75f5 | ||
|
|
fb4ab44aa5 | ||
|
|
71216b1458 | ||
|
|
c969ff4011 | ||
|
|
dc45eb6ed4 | ||
|
|
29aaaae4b4 | ||
|
|
1fe53b9549 | ||
|
|
6274dbebc0 | ||
|
|
a1cd3ab6fa | ||
|
|
c058cb8848 | ||
|
|
029061177b | ||
|
|
9c8e813642 | ||
|
|
7be61b9657 | ||
|
|
2f67d24ec9 | ||
|
|
17d6f1cdf1 | ||
|
|
a0c34b40c6 | ||
|
|
00856f8cb9 | ||
|
|
3df6387ee0 | ||
|
|
fe1c826b1b | ||
|
|
0bda101d4d | ||
|
|
5e738129d9 | ||
|
|
39b5014add | ||
|
|
3c6560ade9 | ||
|
|
183d1a91c1 | ||
|
|
d711ab8b1f | ||
|
|
6c1a48952e | ||
|
|
a343aa5eb0 | ||
|
|
273a0b0b99 | ||
|
|
2f1e64dc1b | ||
|
|
a46d4ecd3e | ||
|
|
5f04954a5c | ||
|
|
d065a7ca22 | ||
|
|
2f8373f9c5 | ||
|
|
2d36e98246 | ||
|
|
f2d8f1ee60 | ||
|
|
e7881c350c | ||
|
|
ffe3e68c35 | ||
|
|
4d6fafd778 | ||
|
|
2bbf77ab4c |
@@ -1,43 +0,0 @@
|
||||
From dbdd2906c0b3a967ca28c6b870b46f905c170661 Mon Sep 17 00:00:00 2001
|
||||
From: Park Ju Hyung <qkrwngud825@gmail.com>
|
||||
Date: Wed, 13 Mar 2019 13:36:37 +0900
|
||||
Subject: [PATCH] setlocalversion: don't check for uncommitted changes
|
||||
|
||||
I ofter push after the build is done and I hate seeing "-dirty"
|
||||
|
||||
Signed-off-by: Park Ju Hyung <qkrwngud825@gmail.com>
|
||||
Signed-off-by: Danny Lin <danny@kdrag0n.dev>
|
||||
Signed-off-by: Divyanshu-Modi <divyan.m05@gmail.com>
|
||||
Change-Id: I240c516520879da680794fd144b1f273f9e21e13
|
||||
Signed-off-by: Divyanshu-Modi <divyan.m05@gmail.com>
|
||||
---
|
||||
scripts/setlocalversion | 13 -------------
|
||||
1 file changed, 13 deletions(-)
|
||||
|
||||
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
|
||||
index 842936656b84..ef27a273ebf5 100755
|
||||
--- a/scripts/setlocalversion
|
||||
+++ b/scripts/setlocalversion
|
||||
@@ -107,19 +107,6 @@ scm_version()
|
||||
printf -- '-svn%s' "$(git svn find-rev $head)"
|
||||
fi
|
||||
|
||||
- # Check for uncommitted changes.
|
||||
- # First, with git-status, but --no-optional-locks is only
|
||||
- # supported in git >= 2.14, so fall back to git-diff-index if
|
||||
- # it fails. Note that git-diff-index does not refresh the
|
||||
- # index, so it may give misleading results. See
|
||||
- # git-update-index(1), git-diff-index(1), and git-status(1).
|
||||
- if {
|
||||
- git --no-optional-locks status -uno --porcelain 2>/dev/null ||
|
||||
- git diff-index --name-only HEAD
|
||||
- } | grep -qvE '^(.. )?scripts/package'; then
|
||||
- printf '%s' -dirty
|
||||
- fi
|
||||
-
|
||||
# All done with git
|
||||
return
|
||||
fi
|
||||
--
|
||||
2.37.2
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
From bbb9e7fb1ccadac47b58ba615e6874ddeaa9e628 Mon Sep 17 00:00:00 2001
|
||||
From: Park Ju Hyung <qkrwngud825@gmail.com>
|
||||
Date: Wed, 13 Mar 2019 13:36:37 +0900
|
||||
Subject: [PATCH] setlocalversion: don't check for uncommitted changes
|
||||
|
||||
I ofter push after the build is done and I hate seeing "-dirty"
|
||||
|
||||
Signed-off-by: Park Ju Hyung <qkrwngud825@gmail.com>
|
||||
Signed-off-by: Danny Lin <danny@kdrag0n.dev>
|
||||
Signed-off-by: Divyanshu-Modi <divyan.m05@gmail.com>
|
||||
Change-Id: I240c516520879da680794fd144b1f273f9e21e13
|
||||
Signed-off-by: Divyanshu-Modi <divyan.m05@gmail.com>
|
||||
---
|
||||
scripts/setlocalversion | 16 ----------------
|
||||
1 file changed, 16 deletions(-)
|
||||
|
||||
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
|
||||
index 1b733ae4c..2a3ea7684 100755
|
||||
--- a/scripts/setlocalversion
|
||||
+++ b/scripts/setlocalversion
|
||||
@@ -90,22 +90,6 @@ scm_version()
|
||||
printf '%s%s' -g "$(echo $head | cut -c1-12)"
|
||||
fi
|
||||
|
||||
- # Check for uncommitted changes.
|
||||
- # This script must avoid any write attempt to the source tree,
|
||||
- # which might be read-only.
|
||||
- # You cannot use 'git describe --dirty' because it tries to
|
||||
- # create .git/index.lock .
|
||||
- # First, with git-status, but --no-optional-locks is only
|
||||
- # supported in git >= 2.14, so fall back to git-diff-index if
|
||||
- # it fails. Note that git-diff-index does not refresh the
|
||||
- # index, so it may give misleading results. See
|
||||
- # git-update-index(1), git-diff-index(1), and git-status(1).
|
||||
- if {
|
||||
- git --no-optional-locks status -uno --porcelain 2>/dev/null ||
|
||||
- git diff-index --name-only HEAD
|
||||
- } | read dummy; then
|
||||
- printf '%s' -dirty
|
||||
- fi
|
||||
fi
|
||||
}
|
||||
|
||||
--
|
||||
2.37.2
|
||||
|
||||
2
.github/scripts/build_a12.sh
vendored
2
.github/scripts/build_a12.sh
vendored
@@ -37,7 +37,7 @@ build_from_image() {
|
||||
echo '[+] Compress images'
|
||||
for image in boot*.img; do
|
||||
$GZIP -n -f -9 "$image"
|
||||
mv "$image".gz ksu-"$VERSION"-"$1"-"$image".gz
|
||||
mv "$image".gz "${1//Image-/}"-"$image".gz
|
||||
done
|
||||
|
||||
echo "[+] Images to upload"
|
||||
|
||||
2
.github/scripts/build_a13.sh
vendored
2
.github/scripts/build_a13.sh
vendored
@@ -24,7 +24,7 @@ build_from_image() {
|
||||
echo '[+] Compress images'
|
||||
for image in boot*.img; do
|
||||
$GZIP -n -f -9 "$image"
|
||||
mv "$image".gz ksu-"$VERSION"-"$1"-"$image".gz
|
||||
mv "$image".gz "${1//Image-/}"-"$image".gz
|
||||
done
|
||||
|
||||
echo '[+] Images to upload'
|
||||
|
||||
103
.github/workflows/build-WSA-5.10.117-kernel.yml
vendored
103
.github/workflows/build-WSA-5.10.117-kernel.yml
vendored
@@ -1,103 +0,0 @@
|
||||
name: Build WSA-5.10.117-Kernel
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- ".github/workflows/build-WSA-5.10.117-kernel.yml"
|
||||
- "kernel/**"
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "kernel/**"
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64, arm64]
|
||||
version: [5.10.117.2]
|
||||
include:
|
||||
- file_name: "bzImage"
|
||||
make_config: "config-wsa"
|
||||
arch: x86_64
|
||||
- file_name: "Image"
|
||||
cross_compile: "aarch64-linux-gnu"
|
||||
make_config: "config-wsa-arm64"
|
||||
arch: arm64
|
||||
|
||||
name: Build WSA-Kernel-${{ matrix.version }}-${{ matrix.arch }}
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
|
||||
CCACHE_NOHASHDIR: "true"
|
||||
CCACHE_MAXSIZE: "2G"
|
||||
CCACHE_HARDLINK: "true"
|
||||
|
||||
steps:
|
||||
- name: Install Build Tools
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y --no-install-recommends bc bison build-essential ca-certificates flex git gnupg libelf-dev libssl-dev lsb-release software-properties-common wget libncurses-dev binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu nuget
|
||||
export LLVM_VERSION=12
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh $LLVM_VERSION
|
||||
rm ./llvm.sh
|
||||
sudo ln -s --force /usr/bin/clang-$LLVM_VERSION /usr/bin/clang
|
||||
sudo ln -s --force /usr/bin/ld.lld-$LLVM_VERSION /usr/bin/ld.lld
|
||||
sudo ln -s --force /usr/bin/llvm-objdump-$LLVM_VERSION /usr/bin/llvm-objdump
|
||||
sudo ln -s --force /usr/bin/llvm-ar-$LLVM_VERSION /usr/bin/llvm-ar
|
||||
sudo ln -s --force /usr/bin/llvm-nm-$LLVM_VERSION /usr/bin/llvm-nm
|
||||
sudo ln -s --force /usr/bin/llvm-strip-$LLVM_VERSION /usr/bin/llvm-strip
|
||||
sudo ln -s --force /usr/bin/llvm-objcopy-$LLVM_VERSION /usr/bin/llvm-objcopy
|
||||
sudo ln -s --force /usr/bin/llvm-readelf-$LLVM_VERSION /usr/bin/llvm-readelf
|
||||
sudo ln -s --force /usr/bin/clang++-$LLVM_VERSION /usr/bin/clang++
|
||||
|
||||
- name: Checkout KernelSU
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: KernelSU
|
||||
ref: main
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup kernel source
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: microsoft/WSA-Linux-Kernel
|
||||
ref: android-lts/latte/${{ matrix.version }}
|
||||
path: WSA-Linux-Kernel
|
||||
|
||||
- name: Setup Ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: WSA-Kernel-${{ matrix.version }}-${{ matrix.arch }}
|
||||
save: ${{ github.event_name != 'pull_request' }}
|
||||
|
||||
- name: Setup KernelSU
|
||||
working-directory: WSA-Linux-Kernel
|
||||
run: |
|
||||
echo "[+] KernelSU setup"
|
||||
KERNEL_ROOT=$GITHUB_WORKSPACE/WSA-Linux-Kernel
|
||||
echo "[+] KERNEL_ROOT: $KERNEL_ROOT"
|
||||
echo "[+] Copy KernelSU driver to $KERNEL_ROOT/drivers"
|
||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $KERNEL_ROOT/drivers/kernelsu
|
||||
echo "[+] Add KernelSU driver to Makefile"
|
||||
DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile
|
||||
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
|
||||
echo "[+] Apply KernelSU patches"
|
||||
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/5.10/*.patch
|
||||
echo "[+] KernelSU setup done."
|
||||
|
||||
- name: Build Kernel
|
||||
working-directory: WSA-Linux-Kernel
|
||||
run: |
|
||||
cp configs/wsa/${{ matrix.make_config }}-5.10 .config
|
||||
make -j`nproc` LLVM=1 ARCH=${{ matrix.arch }} CROSS_COMPILE=${{ matrix.cross_compile }} ${{ matrix.file_name }} CCACHE="/usr/bin/ccache"
|
||||
|
||||
- name: Upload kernel-${{ matrix.arch }}-${{ matrix.version }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: kernel-WSA-${{ matrix.arch }}-${{ matrix.version }}
|
||||
path: WSA-Linux-Kernel/arch/${{ matrix.arch }}/boot/${{ matrix.file_name }}
|
||||
12
.github/workflows/build-kernel-a12.yml
vendored
12
.github/workflows/build-kernel-a12.yml
vendored
@@ -21,20 +21,14 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- sub_level: 66
|
||||
os_patch_level: 2021-11
|
||||
- sub_level: 81
|
||||
os_patch_level: 2022-03
|
||||
- sub_level: 101
|
||||
os_patch_level: 2022-05
|
||||
- sub_level: 110
|
||||
os_patch_level: 2022-07
|
||||
- sub_level: 136
|
||||
os_patch_level: 2022-11
|
||||
- sub_level: 149
|
||||
os_patch_level: 2023-01
|
||||
- sub_level: 160
|
||||
os_patch_level: 2023-02
|
||||
os_patch_level: 2023-03
|
||||
- sub_level: 168
|
||||
os_patch_level: 2023-04
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
|
||||
160
.github/workflows/build-kernel-wsa.yml
vendored
Normal file
160
.github/workflows/build-kernel-wsa.yml
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
name: Build Kernel - WSA
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-wsa.yml"
|
||||
- "kernel/**"
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-wsa.yml"
|
||||
- "kernel/**"
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64, arm64]
|
||||
version: ["5.10.117.2", "5.15.78.1", "5.15.94.1"]
|
||||
include:
|
||||
- arch: x86_64
|
||||
file_name: "bzImage"
|
||||
- arch: arm64
|
||||
file_name: "Image"
|
||||
cross_compile: "aarch64-linux-gnu"
|
||||
- version: "5.10.117.2"
|
||||
arch: x86_64
|
||||
make_config: config-wsa-5.10
|
||||
- version: "5.10.117.2"
|
||||
arch: arm64
|
||||
make_config: config-wsa-arm64-5.10
|
||||
- version: "5.15.78.1"
|
||||
arch: x86_64
|
||||
make_config: config-wsa-x64
|
||||
- version: "5.15.78.1"
|
||||
arch: arm64
|
||||
make_config: config-wsa-arm64
|
||||
- version: "5.15.94.1"
|
||||
arch: x86_64
|
||||
make_config: config-wsa-x64
|
||||
- version: "5.15.94.1"
|
||||
arch: arm64
|
||||
make_config: config-wsa-arm64
|
||||
- version: "5.10.117.2"
|
||||
device_code: latte
|
||||
kernel_version: "5.10"
|
||||
- version: "5.15.78.1"
|
||||
device_code: latte-2
|
||||
kernel_version: "5.15"
|
||||
- version: "5.15.94.1"
|
||||
device_code: latte-2
|
||||
kernel_version: "5.15"
|
||||
|
||||
|
||||
name: Build WSA-Kernel-${{ matrix.version }}-${{ matrix.arch }}
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
|
||||
CCACHE_NOHASHDIR: "true"
|
||||
CCACHE_HARDLINK: "true"
|
||||
|
||||
steps:
|
||||
- name: Install Build Tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends bc bison build-essential ca-certificates flex git gnupg libelf-dev libssl-dev lsb-release software-properties-common wget libncurses-dev binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu nuget gzip
|
||||
export LLVM_VERSION=12
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh $LLVM_VERSION
|
||||
rm ./llvm.sh
|
||||
sudo ln -s --force /usr/bin/clang-$LLVM_VERSION /usr/bin/clang
|
||||
sudo ln -s --force /usr/bin/ld.lld-$LLVM_VERSION /usr/bin/ld.lld
|
||||
sudo ln -s --force /usr/bin/llvm-objdump-$LLVM_VERSION /usr/bin/llvm-objdump
|
||||
sudo ln -s --force /usr/bin/llvm-ar-$LLVM_VERSION /usr/bin/llvm-ar
|
||||
sudo ln -s --force /usr/bin/llvm-nm-$LLVM_VERSION /usr/bin/llvm-nm
|
||||
sudo ln -s --force /usr/bin/llvm-strip-$LLVM_VERSION /usr/bin/llvm-strip
|
||||
sudo ln -s --force /usr/bin/llvm-objcopy-$LLVM_VERSION /usr/bin/llvm-objcopy
|
||||
sudo ln -s --force /usr/bin/llvm-readelf-$LLVM_VERSION /usr/bin/llvm-readelf
|
||||
sudo ln -s --force /usr/bin/clang++-$LLVM_VERSION /usr/bin/clang++
|
||||
|
||||
- name: Checkout KernelSU
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: KernelSU
|
||||
ref: main
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup kernel source
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: microsoft/WSA-Linux-Kernel
|
||||
ref: android-lts/${{ matrix.device_code }}/${{ matrix.version }}
|
||||
path: WSA-Linux-Kernel
|
||||
|
||||
- name: Setup Ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: WSA-Kernel-${{ matrix.version }}-${{ matrix.arch }}
|
||||
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||
max-size: 2G
|
||||
|
||||
- name: Setup KernelSU
|
||||
working-directory: WSA-Linux-Kernel
|
||||
run: |
|
||||
echo "[+] KernelSU setup"
|
||||
KERNEL_ROOT=$GITHUB_WORKSPACE/WSA-Linux-Kernel
|
||||
echo "[+] KERNEL_ROOT: $KERNEL_ROOT"
|
||||
echo "[+] Copy KernelSU driver to $KERNEL_ROOT/drivers"
|
||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $KERNEL_ROOT/drivers/kernelsu
|
||||
echo "[+] Add KernelSU driver to Makefile"
|
||||
DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile
|
||||
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
|
||||
echo "[+] Apply KernelSU patches"
|
||||
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/${{ matrix.kernel_version }}/*.patch
|
||||
echo "[+] KernelSU setup done."
|
||||
cd $GITHUB_WORKSPACE/KernelSU
|
||||
VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||
echo "VERSION: $VERSION"
|
||||
echo "kernelsu_version=$VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Build Kernel
|
||||
working-directory: WSA-Linux-Kernel
|
||||
run: |
|
||||
cp configs/wsa/${{ matrix.make_config }} .config
|
||||
make olddefconfig
|
||||
make -j`nproc` LLVM=1 ARCH=${{ matrix.arch }} CROSS_COMPILE=${{ matrix.cross_compile }} ${{ matrix.file_name }} CCACHE="/usr/bin/ccache"
|
||||
echo "file_path=WSA-Linux-Kernel/arch/${{ matrix.arch }}/boot/${{ matrix.file_name }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload kernel-${{ matrix.arch }}-${{ matrix.version }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: kernel-WSA-${{ matrix.arch }}-${{ matrix.version }}
|
||||
path: "${{ env.file_path }}"
|
||||
|
||||
- name: Post to Telegram
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
||||
env:
|
||||
CHAT_ID: ${{ secrets.CHAT_ID }}
|
||||
CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }}
|
||||
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
|
||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
run: |
|
||||
TITLE=kernel-${{ matrix.arch }}-WSA-${{ matrix.version }}
|
||||
echo "[+] title: $TITLE"
|
||||
export TITLE
|
||||
export VERSION="${{ env.kernelsu_version }}"
|
||||
echo "[+] Compress images"
|
||||
gzip -n -f -9 "${{ env.file_path }}"
|
||||
echo "[+] Image to upload"
|
||||
ls -l "${{ env.file_path }}.gz"
|
||||
if [ -n "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
pip3 install python-telegram-bot
|
||||
python3 "$GITHUB_WORKSPACE/KernelSU/scripts/ksubot.py" "${{ env.file_path }}.gz"
|
||||
fi
|
||||
177
.github/workflows/build-manager.yml
vendored
177
.github/workflows/build-manager.yml
vendored
@@ -1,16 +1,18 @@
|
||||
name: Build Manager
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
paths:
|
||||
- '.github/workflows/build-manager.yml'
|
||||
- 'manager/**'
|
||||
- 'userspace/ksud/**'
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
paths:
|
||||
- 'manager/**'
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
build-ksud:
|
||||
strategy:
|
||||
@@ -21,87 +23,104 @@ jobs:
|
||||
uses: ./.github/workflows/ksud.yml
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
|
||||
build-manager:
|
||||
needs: build-ksud
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./manager
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup need_upload
|
||||
id: need_upload
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
echo "UPLOAD=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: set up JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'temurin'
|
||||
cache: gradle
|
||||
- uses: nttld/setup-ndk@v1
|
||||
with:
|
||||
ndk-version: r25b
|
||||
local-cache: true
|
||||
- name: Extract keystore
|
||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
|
||||
echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' >> sign.properties
|
||||
echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}' >> sign.properties
|
||||
echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}' >> sign.properties
|
||||
echo KEYSTORE_FILE='../key.jks' >> sign.properties
|
||||
echo ${{ secrets.KEYSTORE }} | base64 --decode > key.jks
|
||||
fi
|
||||
- name: Download arm64 ksud
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ksud-aarch64-linux-android
|
||||
path: .
|
||||
- name: Download x86_64 ksud
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ksud-x86_64-linux-android
|
||||
path: .
|
||||
- name: Copy ksud to app jniLibs
|
||||
run: |
|
||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||
mkdir -p app/src/main/jniLibs/x86_64
|
||||
cp -f ../aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud.so
|
||||
cp -f ../x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud.so
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew clean assembleRelease
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: manager
|
||||
path: manager/app/build/outputs/apk/release/*.apk
|
||||
- name: Setup mutex for uploading
|
||||
uses: ben-z/gh-action-mutex@v1.0-alpha-7
|
||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
||||
- name: Upload to telegram
|
||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
||||
env:
|
||||
CHAT_ID: ${{ secrets.CHAT_ID }}
|
||||
CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }}
|
||||
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
|
||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
TITLE: Manager
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
export VERSION=$(git rev-list --count HEAD)
|
||||
APK=$(find ./app/build/outputs/apk/release -name "*.apk")
|
||||
pip3 install python-telegram-bot
|
||||
python3 $GITHUB_WORKSPACE/scripts/ksubot.py $APK
|
||||
fi
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup need_upload
|
||||
id: need_upload
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
echo "UPLOAD=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Write key
|
||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
|
||||
echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' >> gradle.properties
|
||||
echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}' >> gradle.properties
|
||||
echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}' >> gradle.properties
|
||||
echo KEYSTORE_FILE='../key.jks' >> gradle.properties
|
||||
echo ${{ secrets.KEYSTORE }} | base64 --decode > key.jks
|
||||
fi
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: "17"
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
gradle-home-cache-cleanup: true
|
||||
|
||||
- name: Download arm64 ksud
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ksud-aarch64-linux-android
|
||||
path: .
|
||||
|
||||
- name: Download x86_64 ksud
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ksud-x86_64-linux-android
|
||||
path: .
|
||||
|
||||
- name: Copy ksud to app jniLibs
|
||||
run: |
|
||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||
mkdir -p app/src/main/jniLibs/x86_64
|
||||
cp -f ../aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud.so
|
||||
cp -f ../x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud.so
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
echo 'org.gradle.parallel=true' >> gradle.properties
|
||||
echo 'org.gradle.vfs.watch=true' >> gradle.properties
|
||||
echo 'org.gradle.jvmargs=-Xmx2048m' >> gradle.properties
|
||||
echo 'android.native.buildOutput=verbose' >> gradle.properties
|
||||
sed -i 's/org.gradle.configuration-cache=true//g' gradle.properties
|
||||
./gradlew clean assembleRelease
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: manager
|
||||
path: manager/app/build/outputs/apk/release/*.apk
|
||||
|
||||
- name: Setup mutex for uploading
|
||||
uses: ben-z/gh-action-mutex@v1.0-alpha-7
|
||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
||||
|
||||
- name: Upload to telegram
|
||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
||||
env:
|
||||
CHAT_ID: ${{ secrets.CHAT_ID }}
|
||||
CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }}
|
||||
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
|
||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
TITLE: Manager
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
export VERSION=$(git rev-list --count HEAD)
|
||||
APK=$(find ./app/build/outputs/apk/release -name "*.apk")
|
||||
pip3 install python-telegram-bot
|
||||
python3 $GITHUB_WORKSPACE/scripts/ksubot.py $APK
|
||||
fi
|
||||
|
||||
22
.github/workflows/clippy-pr.yml
vendored
22
.github/workflows/clippy-pr.yml
vendored
@@ -1,22 +0,0 @@
|
||||
name: Clippy check for pull request
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
paths:
|
||||
- '.github/workflows/clippy-pr.yml'
|
||||
- 'userspace/ksud/**'
|
||||
|
||||
permissions:
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: giraffate/clippy-action@v1
|
||||
with:
|
||||
workdir: userspace/ksud
|
||||
12
.github/workflows/clippy.yml
vendored
12
.github/workflows/clippy.yml
vendored
@@ -3,7 +3,13 @@ name: Clippy check
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- main
|
||||
paths:
|
||||
- '.github/workflows/clippy.yml'
|
||||
- 'userspace/ksud/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- '.github/workflows/clippy.yml'
|
||||
- 'userspace/ksud/**'
|
||||
@@ -26,5 +32,5 @@ jobs:
|
||||
|
||||
- name: Run clippy
|
||||
run: |
|
||||
cross clippy --manifest-path userspace/ksud/Cargo.toml --target aarch64-linux-android
|
||||
cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-linux-android
|
||||
cross clippy --manifest-path userspace/ksud/Cargo.toml --target aarch64-linux-android --release
|
||||
cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-linux-android --release
|
||||
|
||||
20
.github/workflows/gki-kernel.yml
vendored
20
.github/workflows/gki-kernel.yml
vendored
@@ -64,7 +64,6 @@ jobs:
|
||||
env:
|
||||
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
|
||||
CCACHE_NOHASHDIR: "true"
|
||||
CCACHE_MAXSIZE: "2G"
|
||||
CCACHE_HARDLINK: "true"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -72,13 +71,6 @@ jobs:
|
||||
path: KernelSU
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: hendrikmuhs/ccache-action@v1.2
|
||||
if: inputs.use_cache == true
|
||||
with:
|
||||
key: ccache-aarch64-${{ inputs.version_name }}
|
||||
append-timestamp: false
|
||||
save: ${{ github.event_name != 'pull_request' }}
|
||||
|
||||
- name: Setup need_upload
|
||||
id: need_upload
|
||||
run: |
|
||||
@@ -111,6 +103,8 @@ jobs:
|
||||
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
|
||||
echo "[+] Apply KernelSU patches"
|
||||
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch
|
||||
echo "Patch script/setlocalversion"
|
||||
sed -i 's/-dirty//g' $GKI_ROOT/common/scripts/setlocalversion
|
||||
echo "[+] KernelSU setup done."
|
||||
|
||||
- name: Symbol magic
|
||||
@@ -124,6 +118,14 @@ jobs:
|
||||
echo "[+] Add KernelSU symbols"
|
||||
cat $KSU_ROOT/kernel/export_symbol.txt | awk '{sub("[ \t]+","");print " "$0}' >> $SYMBOL_LIST
|
||||
|
||||
- name: Setup ccache
|
||||
if: inputs.use_cache == true
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: gki-kernel-aarch64-${{ inputs.version_name }}
|
||||
max-size: 2G
|
||||
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||
|
||||
- name: Build boot.img
|
||||
working-directory: android-kernel
|
||||
run: CCACHE="/usr/bin/ccache" LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
|
||||
@@ -149,4 +151,4 @@ jobs:
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: AnyKernel3-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
|
||||
path: ./AnyKernel3/*
|
||||
path: ./AnyKernel3/*
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
build-a13-kernel:
|
||||
uses: ./.github/workflows/build-kernel-a13.yml
|
||||
build-wsa-kernel:
|
||||
uses: ./.github/workflows/build-WSA-5.10.117-kernel.yml
|
||||
uses: ./.github/workflows/build-kernel-wsa.yml
|
||||
release:
|
||||
needs:
|
||||
- build-manager
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
.vscode/
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
**English** | [中文](README_CN.md)
|
||||
**English** | [简体中文](README_CN.md) | [繁體中文](README_TW.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | **中文**
|
||||
[English](README.md) | **简体中文** | [繁體中文](README_TW.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
@@ -37,6 +37,6 @@ WSA 和运行在容器上的 Android 也可以与 KernelSU 一起工作。
|
||||
## 鸣谢
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/):KernelSU 的灵感。
|
||||
- [true](https://github.com/brevent/genuine/):apk v2 签名验证。
|
||||
- [genuine](https://github.com/brevent/genuine/):apk v2 签名验证。
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 rootkit 技巧。
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk):sepolicy 的实现。
|
||||
|
||||
42
README_TW.md
Normal file
42
README_TW.md
Normal file
@@ -0,0 +1,42 @@
|
||||
[English](README.md) | [简体中文](README_CN.md) | **繁體中文**
|
||||
|
||||
# KernelSU
|
||||
|
||||
一個基於核心的 Android 裝置 Root 解決方案
|
||||
|
||||
## 功能
|
||||
|
||||
- 基於核心的 Su 和 Root 存取權管理。
|
||||
- 基於 Overlayfs 的模組系統。
|
||||
|
||||
## 相容性狀態
|
||||
|
||||
KernelSU 官方支援 Android GKI 2.0 的裝置 (核心版本 5.10+);舊版核心同樣相容 (最低 4.14+),但需要自行編譯核心。
|
||||
|
||||
WSA 和執行在容器中的 Android 也可以與 KernelSU 一同運作。
|
||||
|
||||
目前支援架構:`arm64-v8a` 和 `x86_64`
|
||||
|
||||
## 使用方法
|
||||
|
||||
[安裝教學](https://kernelsu.org/zh_TW/guide/installation.html)
|
||||
|
||||
## 建置
|
||||
|
||||
[如何建置?](https://kernelsu.org/zh_TW/guide/how-to-build.html)
|
||||
|
||||
### 討論
|
||||
|
||||
- Telegram:[@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## 授權
|
||||
|
||||
- 目錄 `kernel` 下所有檔案為 [GPL-2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
- 除 `kernel` 目錄的其他部分均為 [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
## 致謝
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/):KernelSU 的靈感。
|
||||
- [genuine](https://github.com/brevent/genuine/):apk v2 簽章驗證。
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 rootkit 技巧。
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk):sepolicy 實作。
|
||||
548
kernel/.clang-format
Normal file
548
kernel/.clang-format
Normal file
@@ -0,0 +1,548 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# clang-format configuration file. Intended for clang-format >= 4.
|
||||
#
|
||||
# For more information, see:
|
||||
#
|
||||
# Documentation/process/clang-format.rst
|
||||
# https://clang.llvm.org/docs/ClangFormat.html
|
||||
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
#
|
||||
---
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
#AlignEscapedNewlines: Left # Unknown to clang-format-4.0
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
#AfterExternBlock: false # Unknown to clang-format-5.0
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
#SplitEmptyFunction: true # Unknown to clang-format-4.0
|
||||
#SplitEmptyRecord: true # Unknown to clang-format-4.0
|
||||
#SplitEmptyNamespace: true # Unknown to clang-format-4.0
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Custom
|
||||
#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: false
|
||||
ColumnLimit: 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
#CompactNamespaces: false # Unknown to clang-format-4.0
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 8
|
||||
ContinuationIndentWidth: 8
|
||||
Cpp11BracedListStyle: false
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
#FixNamespaceComments: false # Unknown to clang-format-4.0
|
||||
|
||||
# Taken from:
|
||||
# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \
|
||||
# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \
|
||||
# | sort | uniq
|
||||
ForEachMacros:
|
||||
- 'apei_estatus_for_each_section'
|
||||
- 'ata_for_each_dev'
|
||||
- 'ata_for_each_link'
|
||||
- '__ata_qc_for_each'
|
||||
- 'ata_qc_for_each'
|
||||
- 'ata_qc_for_each_raw'
|
||||
- 'ata_qc_for_each_with_internal'
|
||||
- 'ax25_for_each'
|
||||
- 'ax25_uid_for_each'
|
||||
- '__bio_for_each_bvec'
|
||||
- 'bio_for_each_bvec'
|
||||
- 'bio_for_each_bvec_all'
|
||||
- 'bio_for_each_integrity_vec'
|
||||
- '__bio_for_each_segment'
|
||||
- 'bio_for_each_segment'
|
||||
- 'bio_for_each_segment_all'
|
||||
- 'bio_list_for_each'
|
||||
- 'bip_for_each_vec'
|
||||
- 'bitmap_for_each_clear_region'
|
||||
- 'bitmap_for_each_set_region'
|
||||
- 'blkg_for_each_descendant_post'
|
||||
- 'blkg_for_each_descendant_pre'
|
||||
- 'blk_queue_for_each_rl'
|
||||
- 'bond_for_each_slave'
|
||||
- 'bond_for_each_slave_rcu'
|
||||
- 'bpf_for_each_spilled_reg'
|
||||
- 'btree_for_each_safe128'
|
||||
- 'btree_for_each_safe32'
|
||||
- 'btree_for_each_safe64'
|
||||
- 'btree_for_each_safel'
|
||||
- 'card_for_each_dev'
|
||||
- 'cgroup_taskset_for_each'
|
||||
- 'cgroup_taskset_for_each_leader'
|
||||
- 'cpufreq_for_each_entry'
|
||||
- 'cpufreq_for_each_entry_idx'
|
||||
- 'cpufreq_for_each_valid_entry'
|
||||
- 'cpufreq_for_each_valid_entry_idx'
|
||||
- 'css_for_each_child'
|
||||
- 'css_for_each_descendant_post'
|
||||
- 'css_for_each_descendant_pre'
|
||||
- 'device_for_each_child_node'
|
||||
- 'dma_fence_chain_for_each'
|
||||
- 'do_for_each_ftrace_op'
|
||||
- 'drm_atomic_crtc_for_each_plane'
|
||||
- 'drm_atomic_crtc_state_for_each_plane'
|
||||
- 'drm_atomic_crtc_state_for_each_plane_state'
|
||||
- 'drm_atomic_for_each_plane_damage'
|
||||
- 'drm_client_for_each_connector_iter'
|
||||
- 'drm_client_for_each_modeset'
|
||||
- 'drm_connector_for_each_possible_encoder'
|
||||
- 'drm_for_each_bridge_in_chain'
|
||||
- 'drm_for_each_connector_iter'
|
||||
- 'drm_for_each_crtc'
|
||||
- 'drm_for_each_encoder'
|
||||
- 'drm_for_each_encoder_mask'
|
||||
- 'drm_for_each_fb'
|
||||
- 'drm_for_each_legacy_plane'
|
||||
- 'drm_for_each_plane'
|
||||
- 'drm_for_each_plane_mask'
|
||||
- 'drm_for_each_privobj'
|
||||
- 'drm_mm_for_each_hole'
|
||||
- 'drm_mm_for_each_node'
|
||||
- 'drm_mm_for_each_node_in_range'
|
||||
- 'drm_mm_for_each_node_safe'
|
||||
- 'flow_action_for_each'
|
||||
- 'for_each_active_dev_scope'
|
||||
- 'for_each_active_drhd_unit'
|
||||
- 'for_each_active_iommu'
|
||||
- 'for_each_aggr_pgid'
|
||||
- 'for_each_available_child_of_node'
|
||||
- 'for_each_bio'
|
||||
- 'for_each_board_func_rsrc'
|
||||
- 'for_each_bvec'
|
||||
- 'for_each_card_auxs'
|
||||
- 'for_each_card_auxs_safe'
|
||||
- 'for_each_card_components'
|
||||
- 'for_each_card_dapms'
|
||||
- 'for_each_card_pre_auxs'
|
||||
- 'for_each_card_prelinks'
|
||||
- 'for_each_card_rtds'
|
||||
- 'for_each_card_rtds_safe'
|
||||
- 'for_each_card_widgets'
|
||||
- 'for_each_card_widgets_safe'
|
||||
- 'for_each_cgroup_storage_type'
|
||||
- 'for_each_child_of_node'
|
||||
- 'for_each_clear_bit'
|
||||
- 'for_each_clear_bit_from'
|
||||
- 'for_each_cmsghdr'
|
||||
- 'for_each_compatible_node'
|
||||
- 'for_each_component_dais'
|
||||
- 'for_each_component_dais_safe'
|
||||
- 'for_each_comp_order'
|
||||
- 'for_each_console'
|
||||
- 'for_each_cpu'
|
||||
- 'for_each_cpu_and'
|
||||
- 'for_each_cpu_not'
|
||||
- 'for_each_cpu_wrap'
|
||||
- 'for_each_dapm_widgets'
|
||||
- 'for_each_dev_addr'
|
||||
- 'for_each_dev_scope'
|
||||
- 'for_each_displayid_db'
|
||||
- 'for_each_dma_cap_mask'
|
||||
- 'for_each_dpcm_be'
|
||||
- 'for_each_dpcm_be_rollback'
|
||||
- 'for_each_dpcm_be_safe'
|
||||
- 'for_each_dpcm_fe'
|
||||
- 'for_each_drhd_unit'
|
||||
- 'for_each_dss_dev'
|
||||
- 'for_each_efi_memory_desc'
|
||||
- 'for_each_efi_memory_desc_in_map'
|
||||
- 'for_each_element'
|
||||
- 'for_each_element_extid'
|
||||
- 'for_each_element_id'
|
||||
- 'for_each_endpoint_of_node'
|
||||
- 'for_each_evictable_lru'
|
||||
- 'for_each_fib6_node_rt_rcu'
|
||||
- 'for_each_fib6_walker_rt'
|
||||
- 'for_each_free_mem_pfn_range_in_zone'
|
||||
- 'for_each_free_mem_pfn_range_in_zone_from'
|
||||
- 'for_each_free_mem_range'
|
||||
- 'for_each_free_mem_range_reverse'
|
||||
- 'for_each_func_rsrc'
|
||||
- 'for_each_hstate'
|
||||
- 'for_each_if'
|
||||
- 'for_each_iommu'
|
||||
- 'for_each_ip_tunnel_rcu'
|
||||
- 'for_each_irq_nr'
|
||||
- 'for_each_link_codecs'
|
||||
- 'for_each_link_cpus'
|
||||
- 'for_each_link_platforms'
|
||||
- 'for_each_lru'
|
||||
- 'for_each_matching_node'
|
||||
- 'for_each_matching_node_and_match'
|
||||
- 'for_each_member'
|
||||
- 'for_each_mem_region'
|
||||
- 'for_each_memblock_type'
|
||||
- 'for_each_memcg_cache_index'
|
||||
- 'for_each_mem_pfn_range'
|
||||
- '__for_each_mem_range'
|
||||
- 'for_each_mem_range'
|
||||
- '__for_each_mem_range_rev'
|
||||
- 'for_each_mem_range_rev'
|
||||
- 'for_each_migratetype_order'
|
||||
- 'for_each_msi_entry'
|
||||
- 'for_each_msi_entry_safe'
|
||||
- 'for_each_net'
|
||||
- 'for_each_net_continue_reverse'
|
||||
- 'for_each_netdev'
|
||||
- 'for_each_netdev_continue'
|
||||
- 'for_each_netdev_continue_rcu'
|
||||
- 'for_each_netdev_continue_reverse'
|
||||
- 'for_each_netdev_feature'
|
||||
- 'for_each_netdev_in_bond_rcu'
|
||||
- 'for_each_netdev_rcu'
|
||||
- 'for_each_netdev_reverse'
|
||||
- 'for_each_netdev_safe'
|
||||
- 'for_each_net_rcu'
|
||||
- 'for_each_new_connector_in_state'
|
||||
- 'for_each_new_crtc_in_state'
|
||||
- 'for_each_new_mst_mgr_in_state'
|
||||
- 'for_each_new_plane_in_state'
|
||||
- 'for_each_new_private_obj_in_state'
|
||||
- 'for_each_node'
|
||||
- 'for_each_node_by_name'
|
||||
- 'for_each_node_by_type'
|
||||
- 'for_each_node_mask'
|
||||
- 'for_each_node_state'
|
||||
- 'for_each_node_with_cpus'
|
||||
- 'for_each_node_with_property'
|
||||
- 'for_each_nonreserved_multicast_dest_pgid'
|
||||
- 'for_each_of_allnodes'
|
||||
- 'for_each_of_allnodes_from'
|
||||
- 'for_each_of_cpu_node'
|
||||
- 'for_each_of_pci_range'
|
||||
- 'for_each_old_connector_in_state'
|
||||
- 'for_each_old_crtc_in_state'
|
||||
- 'for_each_old_mst_mgr_in_state'
|
||||
- 'for_each_oldnew_connector_in_state'
|
||||
- 'for_each_oldnew_crtc_in_state'
|
||||
- 'for_each_oldnew_mst_mgr_in_state'
|
||||
- 'for_each_oldnew_plane_in_state'
|
||||
- 'for_each_oldnew_plane_in_state_reverse'
|
||||
- 'for_each_oldnew_private_obj_in_state'
|
||||
- 'for_each_old_plane_in_state'
|
||||
- 'for_each_old_private_obj_in_state'
|
||||
- 'for_each_online_cpu'
|
||||
- 'for_each_online_node'
|
||||
- 'for_each_online_pgdat'
|
||||
- 'for_each_pci_bridge'
|
||||
- 'for_each_pci_dev'
|
||||
- 'for_each_pci_msi_entry'
|
||||
- 'for_each_pcm_streams'
|
||||
- 'for_each_physmem_range'
|
||||
- 'for_each_populated_zone'
|
||||
- 'for_each_possible_cpu'
|
||||
- 'for_each_present_cpu'
|
||||
- 'for_each_prime_number'
|
||||
- 'for_each_prime_number_from'
|
||||
- 'for_each_process'
|
||||
- 'for_each_process_thread'
|
||||
- 'for_each_property_of_node'
|
||||
- 'for_each_registered_fb'
|
||||
- 'for_each_requested_gpio'
|
||||
- 'for_each_requested_gpio_in_range'
|
||||
- 'for_each_reserved_mem_range'
|
||||
- 'for_each_reserved_mem_region'
|
||||
- 'for_each_rtd_codec_dais'
|
||||
- 'for_each_rtd_codec_dais_rollback'
|
||||
- 'for_each_rtd_components'
|
||||
- 'for_each_rtd_cpu_dais'
|
||||
- 'for_each_rtd_cpu_dais_rollback'
|
||||
- 'for_each_rtd_dais'
|
||||
- 'for_each_set_bit'
|
||||
- 'for_each_set_bit_from'
|
||||
- 'for_each_set_clump8'
|
||||
- 'for_each_sg'
|
||||
- 'for_each_sg_dma_page'
|
||||
- 'for_each_sg_page'
|
||||
- 'for_each_sgtable_dma_page'
|
||||
- 'for_each_sgtable_dma_sg'
|
||||
- 'for_each_sgtable_page'
|
||||
- 'for_each_sgtable_sg'
|
||||
- 'for_each_sibling_event'
|
||||
- 'for_each_subelement'
|
||||
- 'for_each_subelement_extid'
|
||||
- 'for_each_subelement_id'
|
||||
- '__for_each_thread'
|
||||
- 'for_each_thread'
|
||||
- 'for_each_unicast_dest_pgid'
|
||||
- 'for_each_wakeup_source'
|
||||
- 'for_each_zone'
|
||||
- 'for_each_zone_zonelist'
|
||||
- 'for_each_zone_zonelist_nodemask'
|
||||
- 'fwnode_for_each_available_child_node'
|
||||
- 'fwnode_for_each_child_node'
|
||||
- 'fwnode_graph_for_each_endpoint'
|
||||
- 'gadget_for_each_ep'
|
||||
- 'genradix_for_each'
|
||||
- 'genradix_for_each_from'
|
||||
- 'hash_for_each'
|
||||
- 'hash_for_each_possible'
|
||||
- 'hash_for_each_possible_rcu'
|
||||
- 'hash_for_each_possible_rcu_notrace'
|
||||
- 'hash_for_each_possible_safe'
|
||||
- 'hash_for_each_rcu'
|
||||
- 'hash_for_each_safe'
|
||||
- 'hctx_for_each_ctx'
|
||||
- 'hlist_bl_for_each_entry'
|
||||
- 'hlist_bl_for_each_entry_rcu'
|
||||
- 'hlist_bl_for_each_entry_safe'
|
||||
- 'hlist_for_each'
|
||||
- 'hlist_for_each_entry'
|
||||
- 'hlist_for_each_entry_continue'
|
||||
- 'hlist_for_each_entry_continue_rcu'
|
||||
- 'hlist_for_each_entry_continue_rcu_bh'
|
||||
- 'hlist_for_each_entry_from'
|
||||
- 'hlist_for_each_entry_from_rcu'
|
||||
- 'hlist_for_each_entry_rcu'
|
||||
- 'hlist_for_each_entry_rcu_bh'
|
||||
- 'hlist_for_each_entry_rcu_notrace'
|
||||
- 'hlist_for_each_entry_safe'
|
||||
- '__hlist_for_each_rcu'
|
||||
- 'hlist_for_each_safe'
|
||||
- 'hlist_nulls_for_each_entry'
|
||||
- 'hlist_nulls_for_each_entry_from'
|
||||
- 'hlist_nulls_for_each_entry_rcu'
|
||||
- 'hlist_nulls_for_each_entry_safe'
|
||||
- 'i3c_bus_for_each_i2cdev'
|
||||
- 'i3c_bus_for_each_i3cdev'
|
||||
- 'ide_host_for_each_port'
|
||||
- 'ide_port_for_each_dev'
|
||||
- 'ide_port_for_each_present_dev'
|
||||
- 'idr_for_each_entry'
|
||||
- 'idr_for_each_entry_continue'
|
||||
- 'idr_for_each_entry_continue_ul'
|
||||
- 'idr_for_each_entry_ul'
|
||||
- 'in_dev_for_each_ifa_rcu'
|
||||
- 'in_dev_for_each_ifa_rtnl'
|
||||
- 'inet_bind_bucket_for_each'
|
||||
- 'inet_lhash2_for_each_icsk_rcu'
|
||||
- 'key_for_each'
|
||||
- 'key_for_each_safe'
|
||||
- 'klp_for_each_func'
|
||||
- 'klp_for_each_func_safe'
|
||||
- 'klp_for_each_func_static'
|
||||
- 'klp_for_each_object'
|
||||
- 'klp_for_each_object_safe'
|
||||
- 'klp_for_each_object_static'
|
||||
- 'kunit_suite_for_each_test_case'
|
||||
- 'kvm_for_each_memslot'
|
||||
- 'kvm_for_each_vcpu'
|
||||
- 'list_for_each'
|
||||
- 'list_for_each_codec'
|
||||
- 'list_for_each_codec_safe'
|
||||
- 'list_for_each_continue'
|
||||
- 'list_for_each_entry'
|
||||
- 'list_for_each_entry_continue'
|
||||
- 'list_for_each_entry_continue_rcu'
|
||||
- 'list_for_each_entry_continue_reverse'
|
||||
- 'list_for_each_entry_from'
|
||||
- 'list_for_each_entry_from_rcu'
|
||||
- 'list_for_each_entry_from_reverse'
|
||||
- 'list_for_each_entry_lockless'
|
||||
- 'list_for_each_entry_rcu'
|
||||
- 'list_for_each_entry_reverse'
|
||||
- 'list_for_each_entry_safe'
|
||||
- 'list_for_each_entry_safe_continue'
|
||||
- 'list_for_each_entry_safe_from'
|
||||
- 'list_for_each_entry_safe_reverse'
|
||||
- 'list_for_each_prev'
|
||||
- 'list_for_each_prev_safe'
|
||||
- 'list_for_each_safe'
|
||||
- 'llist_for_each'
|
||||
- 'llist_for_each_entry'
|
||||
- 'llist_for_each_entry_safe'
|
||||
- 'llist_for_each_safe'
|
||||
- 'mci_for_each_dimm'
|
||||
- 'media_device_for_each_entity'
|
||||
- 'media_device_for_each_intf'
|
||||
- 'media_device_for_each_link'
|
||||
- 'media_device_for_each_pad'
|
||||
- 'nanddev_io_for_each_page'
|
||||
- 'netdev_for_each_lower_dev'
|
||||
- 'netdev_for_each_lower_private'
|
||||
- 'netdev_for_each_lower_private_rcu'
|
||||
- 'netdev_for_each_mc_addr'
|
||||
- 'netdev_for_each_uc_addr'
|
||||
- 'netdev_for_each_upper_dev_rcu'
|
||||
- 'netdev_hw_addr_list_for_each'
|
||||
- 'nft_rule_for_each_expr'
|
||||
- 'nla_for_each_attr'
|
||||
- 'nla_for_each_nested'
|
||||
- 'nlmsg_for_each_attr'
|
||||
- 'nlmsg_for_each_msg'
|
||||
- 'nr_neigh_for_each'
|
||||
- 'nr_neigh_for_each_safe'
|
||||
- 'nr_node_for_each'
|
||||
- 'nr_node_for_each_safe'
|
||||
- 'of_for_each_phandle'
|
||||
- 'of_property_for_each_string'
|
||||
- 'of_property_for_each_u32'
|
||||
- 'pci_bus_for_each_resource'
|
||||
- 'pcm_for_each_format'
|
||||
- 'ping_portaddr_for_each_entry'
|
||||
- 'plist_for_each'
|
||||
- 'plist_for_each_continue'
|
||||
- 'plist_for_each_entry'
|
||||
- 'plist_for_each_entry_continue'
|
||||
- 'plist_for_each_entry_safe'
|
||||
- 'plist_for_each_safe'
|
||||
- 'pnp_for_each_card'
|
||||
- 'pnp_for_each_dev'
|
||||
- 'protocol_for_each_card'
|
||||
- 'protocol_for_each_dev'
|
||||
- 'queue_for_each_hw_ctx'
|
||||
- 'radix_tree_for_each_slot'
|
||||
- 'radix_tree_for_each_tagged'
|
||||
- 'rbtree_postorder_for_each_entry_safe'
|
||||
- 'rdma_for_each_block'
|
||||
- 'rdma_for_each_port'
|
||||
- 'rdma_umem_for_each_dma_block'
|
||||
- 'resource_list_for_each_entry'
|
||||
- 'resource_list_for_each_entry_safe'
|
||||
- 'rhl_for_each_entry_rcu'
|
||||
- 'rhl_for_each_rcu'
|
||||
- 'rht_for_each'
|
||||
- 'rht_for_each_entry'
|
||||
- 'rht_for_each_entry_from'
|
||||
- 'rht_for_each_entry_rcu'
|
||||
- 'rht_for_each_entry_rcu_from'
|
||||
- 'rht_for_each_entry_safe'
|
||||
- 'rht_for_each_from'
|
||||
- 'rht_for_each_rcu'
|
||||
- 'rht_for_each_rcu_from'
|
||||
- '__rq_for_each_bio'
|
||||
- 'rq_for_each_bvec'
|
||||
- 'rq_for_each_segment'
|
||||
- 'scsi_for_each_prot_sg'
|
||||
- 'scsi_for_each_sg'
|
||||
- 'sctp_for_each_hentry'
|
||||
- 'sctp_skb_for_each'
|
||||
- 'shdma_for_each_chan'
|
||||
- '__shost_for_each_device'
|
||||
- 'shost_for_each_device'
|
||||
- 'sk_for_each'
|
||||
- 'sk_for_each_bound'
|
||||
- 'sk_for_each_entry_offset_rcu'
|
||||
- 'sk_for_each_from'
|
||||
- 'sk_for_each_rcu'
|
||||
- 'sk_for_each_safe'
|
||||
- 'sk_nulls_for_each'
|
||||
- 'sk_nulls_for_each_from'
|
||||
- 'sk_nulls_for_each_rcu'
|
||||
- 'snd_array_for_each'
|
||||
- 'snd_pcm_group_for_each_entry'
|
||||
- 'snd_soc_dapm_widget_for_each_path'
|
||||
- 'snd_soc_dapm_widget_for_each_path_safe'
|
||||
- 'snd_soc_dapm_widget_for_each_sink_path'
|
||||
- 'snd_soc_dapm_widget_for_each_source_path'
|
||||
- 'tb_property_for_each'
|
||||
- 'tcf_exts_for_each_action'
|
||||
- 'udp_portaddr_for_each_entry'
|
||||
- 'udp_portaddr_for_each_entry_rcu'
|
||||
- 'usb_hub_for_each_child'
|
||||
- 'v4l2_device_for_each_subdev'
|
||||
- 'v4l2_m2m_for_each_dst_buf'
|
||||
- 'v4l2_m2m_for_each_dst_buf_safe'
|
||||
- 'v4l2_m2m_for_each_src_buf'
|
||||
- 'v4l2_m2m_for_each_src_buf_safe'
|
||||
- 'virtio_device_for_each_vq'
|
||||
- 'while_for_each_ftrace_op'
|
||||
- 'xa_for_each'
|
||||
- 'xa_for_each_marked'
|
||||
- 'xa_for_each_range'
|
||||
- 'xa_for_each_start'
|
||||
- 'xas_for_each'
|
||||
- 'xas_for_each_conflict'
|
||||
- 'xas_for_each_marked'
|
||||
- 'xbc_array_for_each_value'
|
||||
- 'xbc_for_each_key_value'
|
||||
- 'xbc_node_for_each_array_value'
|
||||
- 'xbc_node_for_each_child'
|
||||
- 'xbc_node_for_each_key_value'
|
||||
- 'zorro_for_each_dev'
|
||||
|
||||
#IncludeBlocks: Preserve # Unknown to clang-format-5.0
|
||||
IncludeCategories:
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IndentCaseLabels: false
|
||||
#IndentPPDirectives: None # Unknown to clang-format-5.0
|
||||
IndentWidth: 8
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
|
||||
ObjCBlockIndentWidth: 8
|
||||
ObjCSpaceAfterProperty: true
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
|
||||
# Taken from git's rules
|
||||
#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0
|
||||
PenaltyBreakBeforeFirstCallParameter: 30
|
||||
PenaltyBreakComment: 10
|
||||
PenaltyBreakFirstLessLess: 0
|
||||
PenaltyBreakString: 10
|
||||
PenaltyExcessCharacter: 100
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
|
||||
PointerAlignment: Right
|
||||
ReflowComments: false
|
||||
SortIncludes: false
|
||||
#SortUsingDeclarations: false # Unknown to clang-format-4.0
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0
|
||||
#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0
|
||||
SpaceBeforeParens: ControlStatements
|
||||
#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Cpp03
|
||||
TabWidth: 8
|
||||
UseTab: Always
|
||||
...
|
||||
@@ -1,7 +1,6 @@
|
||||
config KSU
|
||||
tristate "KernelSU module"
|
||||
default y
|
||||
depends on KPROBES
|
||||
depends on OVERLAY_FS
|
||||
help
|
||||
This is the KSU privilege driver for android system.
|
||||
|
||||
@@ -14,7 +14,7 @@ obj-y += kernel_compat.o
|
||||
obj-y += selinux/
|
||||
# .git is a text file while the module is imported by 'git submodule add'.
|
||||
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
|
||||
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH=$$PATH:/usr/bin:/usr/local/bin git rev-list --count HEAD)
|
||||
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git rev-list --count HEAD)
|
||||
ccflags-y += -DKSU_GIT_VERSION=$(KSU_GIT_VERSION)
|
||||
endif
|
||||
|
||||
|
||||
@@ -64,6 +64,8 @@ bool ksu_allow_uid(uid_t uid, bool allow, bool persist)
|
||||
p->uid = uid;
|
||||
p->allow = allow;
|
||||
|
||||
pr_info("allow_uid: %d, allow: %d", uid, allow);
|
||||
|
||||
list_add_tail(&p->list, &allow_list);
|
||||
result = true;
|
||||
|
||||
@@ -102,7 +104,7 @@ bool ksu_get_allow_list(int *array, int *length, bool allow)
|
||||
int i = 0;
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
|
||||
// pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
|
||||
if (p->allow == allow) {
|
||||
array[i++] = p->uid;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "linux/kernel.h"
|
||||
#include "linux/kprobes.h"
|
||||
#include "linux/lsm_hooks.h"
|
||||
#include "linux/nsproxy.h"
|
||||
#include "linux/path.h"
|
||||
#include "linux/printk.h"
|
||||
#include "linux/uaccess.h"
|
||||
@@ -38,14 +39,16 @@ static inline bool is_allow_su()
|
||||
return ksu_is_allow_uid(current_uid().val);
|
||||
}
|
||||
|
||||
static inline bool is_isolated_uid(uid_t uid) {
|
||||
#define FIRST_ISOLATED_UID 99000
|
||||
#define LAST_ISOLATED_UID 99999
|
||||
#define FIRST_APP_ZYGOTE_ISOLATED_UID 90000
|
||||
#define LAST_APP_ZYGOTE_ISOLATED_UID 98999
|
||||
uid_t appid = uid % 100000;
|
||||
return (appid >= FIRST_ISOLATED_UID && appid <= LAST_ISOLATED_UID)
|
||||
|| (appid >= FIRST_APP_ZYGOTE_ISOLATED_UID && appid <= LAST_APP_ZYGOTE_ISOLATED_UID);
|
||||
static inline bool is_isolated_uid(uid_t uid)
|
||||
{
|
||||
#define FIRST_ISOLATED_UID 99000
|
||||
#define LAST_ISOLATED_UID 99999
|
||||
#define FIRST_APP_ZYGOTE_ISOLATED_UID 90000
|
||||
#define LAST_APP_ZYGOTE_ISOLATED_UID 98999
|
||||
uid_t appid = uid % 100000;
|
||||
return (appid >= FIRST_ISOLATED_UID && appid <= LAST_ISOLATED_UID) ||
|
||||
(appid >= FIRST_APP_ZYGOTE_ISOLATED_UID &&
|
||||
appid <= LAST_APP_ZYGOTE_ISOLATED_UID);
|
||||
}
|
||||
|
||||
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
|
||||
@@ -168,14 +171,25 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
}
|
||||
|
||||
// someone wants to be root manager, just check it!
|
||||
// arg3 should be `/data/data/<manager_package_name>`
|
||||
// arg3 should be `/data/user/<userId>/<manager_package_name>`
|
||||
char param[128];
|
||||
const char *prefix = "/data/data/";
|
||||
if (copy_from_user(param, arg3, sizeof(param))) {
|
||||
pr_err("become_manager: copy param err\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// for user 0, it is /data/data
|
||||
// for user 999, it is /data/user/999
|
||||
const char *prefix;
|
||||
char prefixTmp[64];
|
||||
int userId = current_uid().val / 100000;
|
||||
if (userId == 0) {
|
||||
prefix = "/data/data";
|
||||
} else {
|
||||
snprintf(prefixTmp, sizeof(prefixTmp), "/data/user/%d", userId);
|
||||
prefix = prefixTmp;
|
||||
}
|
||||
|
||||
if (startswith(param, (char *)prefix) != 0) {
|
||||
pr_info("become_manager: invalid param: %s\n", param);
|
||||
return 0;
|
||||
@@ -335,28 +349,36 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_appuid(kuid_t uid) {
|
||||
#define PER_USER_RANGE 100000
|
||||
#define FIRST_APPLICATION_UID 10000
|
||||
#define LAST_APPLICATION_UID 19999
|
||||
static bool is_appuid(kuid_t uid)
|
||||
{
|
||||
#define PER_USER_RANGE 100000
|
||||
#define FIRST_APPLICATION_UID 10000
|
||||
#define LAST_APPLICATION_UID 19999
|
||||
|
||||
uid_t appid = uid.val % PER_USER_RANGE;
|
||||
return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID;
|
||||
}
|
||||
|
||||
static bool should_umount(struct path* path) {
|
||||
static bool should_umount(struct path *path)
|
||||
{
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) {
|
||||
pr_info("ignore global mnt namespace process: %d\n", current_uid().val);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) {
|
||||
const char* fstype = path->mnt->mnt_sb->s_type->name;
|
||||
const char *fstype = path->mnt->mnt_sb->s_type->name;
|
||||
return strcmp(fstype, "overlay") == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void try_umount(const char *mnt) {
|
||||
static void try_umount(const char *mnt)
|
||||
{
|
||||
struct path path;
|
||||
int err = kern_path(mnt, 0, &path);
|
||||
if (err) {
|
||||
@@ -369,14 +391,15 @@ static void try_umount(const char *mnt) {
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||
err = path_umount(&path, 0);
|
||||
err = path_umount(&path, MNT_DETACH);
|
||||
if (err) {
|
||||
pr_info("umount %s failed: %d\n", mnt, err);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int ksu_handle_setuid(struct cred *new, const struct cred *old) {
|
||||
int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
||||
{
|
||||
if (!new || !old) {
|
||||
return 0;
|
||||
}
|
||||
@@ -509,7 +532,8 @@ static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
|
||||
}
|
||||
|
||||
static int ksu_task_fix_setuid(struct cred *new, const struct cred *old,
|
||||
int flags) {
|
||||
int flags)
|
||||
{
|
||||
return ksu_handle_setuid(new, old);
|
||||
}
|
||||
|
||||
|
||||
101
kernel/ksud.c
101
kernel/ksud.c
@@ -1,5 +1,6 @@
|
||||
#include "asm/current.h"
|
||||
#include "linux/string.h"
|
||||
#include "linux/compat.h"
|
||||
#include "linux/cred.h"
|
||||
#include "linux/dcache.h"
|
||||
#include "linux/err.h"
|
||||
@@ -23,6 +24,7 @@ static const char KERNEL_SU_RC[] =
|
||||
"\n"
|
||||
|
||||
"on post-fs-data\n"
|
||||
" start logd\n"
|
||||
// We should wait for the post-fs-data finish
|
||||
" exec u:r:su:s0 root -- " KSUD_PATH " post-fs-data\n"
|
||||
"\n"
|
||||
@@ -69,6 +71,69 @@ void on_post_fs_data(void)
|
||||
stop_input_hook();
|
||||
}
|
||||
|
||||
#define MAX_ARG_STRINGS 0x7FFFFFFF
|
||||
struct user_arg_ptr {
|
||||
#ifdef CONFIG_COMPAT
|
||||
bool is_compat;
|
||||
#endif
|
||||
union {
|
||||
const char __user *const __user *native;
|
||||
#ifdef CONFIG_COMPAT
|
||||
const compat_uptr_t __user *compat;
|
||||
#endif
|
||||
} ptr;
|
||||
};
|
||||
|
||||
static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
|
||||
{
|
||||
const char __user *native;
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
if (unlikely(argv.is_compat)) {
|
||||
compat_uptr_t compat;
|
||||
|
||||
if (get_user(compat, argv.ptr.compat + nr))
|
||||
return ERR_PTR(-EFAULT);
|
||||
|
||||
return compat_ptr(compat);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (get_user(native, argv.ptr.native + nr))
|
||||
return ERR_PTR(-EFAULT);
|
||||
|
||||
return native;
|
||||
}
|
||||
|
||||
/*
|
||||
* count() counts the number of strings in array ARGV.
|
||||
*/
|
||||
static int count(struct user_arg_ptr argv, int max)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (argv.ptr.native != NULL) {
|
||||
for (;;) {
|
||||
const char __user *p = get_user_arg_ptr(argv, i);
|
||||
|
||||
if (!p)
|
||||
break;
|
||||
|
||||
if (IS_ERR(p))
|
||||
return -EFAULT;
|
||||
|
||||
if (i >= max)
|
||||
return -E2BIG;
|
||||
++i;
|
||||
|
||||
if (fatal_signal_pending(current))
|
||||
return -ERESTARTNOHAND;
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags)
|
||||
{
|
||||
@@ -82,7 +147,7 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||
static const char app_process[] = "/system/bin/app_process";
|
||||
static bool first_app_process = true;
|
||||
static const char system_bin_init[] = "/system/bin/init";
|
||||
static int init_count = 0;
|
||||
static bool init_second_stage_executed = false;
|
||||
|
||||
if (!filename_ptr)
|
||||
return 0;
|
||||
@@ -94,19 +159,51 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||
|
||||
if (!memcmp(filename->name, system_bin_init,
|
||||
sizeof(system_bin_init) - 1)) {
|
||||
#ifdef __aarch64__
|
||||
// /system/bin/init executed
|
||||
struct user_arg_ptr *ptr = (struct user_arg_ptr*) argv;
|
||||
int argc = count(*ptr, MAX_ARG_STRINGS);
|
||||
pr_info("/system/bin/init argc: %d\n", argc);
|
||||
if (argc > 1 && !init_second_stage_executed) {
|
||||
const char __user *p = get_user_arg_ptr(*ptr, 1);
|
||||
if (p && !IS_ERR(p)) {
|
||||
char first_arg[16];
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
|
||||
strncpy_from_user_nofault(first_arg, p, sizeof(first_arg));
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
|
||||
strncpy_from_unsafe_user(first_arg, p, sizeof(first_arg));
|
||||
#else
|
||||
strncpy_from_user(first_arg, p, sizeof(first_arg));
|
||||
#endif
|
||||
pr_info("first arg: %s\n", first_arg);
|
||||
if (!strcmp(first_arg, "second_stage")) {
|
||||
pr_info("/system/bin/init second_stage executed\n");
|
||||
apply_kernelsu_rules();
|
||||
init_second_stage_executed = true;
|
||||
}
|
||||
} else {
|
||||
pr_err("/system/bin/init parse args err!\n");
|
||||
}
|
||||
}
|
||||
#else
|
||||
// The argument parse is incorrect becuase of the struct user_arg_ptr has 16bytes
|
||||
// and it is passed by value(not pointer), in arm64, it is correct becuase the register
|
||||
// is just arranged correct accidentally, but is not correct in x86_64
|
||||
// i have no device to test, so revert it for x86_64
|
||||
static int init_count = 0;
|
||||
if (++init_count == 2) {
|
||||
// 1: /system/bin/init selinux_setup
|
||||
// 2: /system/bin/init second_stage
|
||||
pr_info("/system/bin/init second_stage executed\n");
|
||||
apply_kernelsu_rules();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (first_app_process &&
|
||||
!memcmp(filename->name, app_process, sizeof(app_process) - 1)) {
|
||||
first_app_process = false;
|
||||
pr_info("exec app_process, /data prepared!\n");
|
||||
pr_info("exec app_process, /data prepared, second_stage: %d\n", init_second_stage_executed);
|
||||
on_post_fs_data(); // we keep this for old ksud
|
||||
stop_execve_hook();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,13 @@ obj-y += selinux.o
|
||||
obj-y += sepolicy.o
|
||||
obj-y += rules.o
|
||||
|
||||
ifeq ($(shell grep -q " current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
|
||||
ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID
|
||||
endif
|
||||
|
||||
ifeq ($(shell grep -q "struct selinux_state " $(srctree)/security/selinux/include/security.h; echo $$?),0)
|
||||
ccflags-y += -DKSU_COMPAT_HAS_SELINUX_STATE
|
||||
endif
|
||||
|
||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion
|
||||
ccflags-y += -Wno-macro-redefined -Wno-declaration-after-statement -Wno-unused-function
|
||||
|
||||
@@ -22,7 +22,7 @@ static struct policydb *get_policydb(void)
|
||||
{
|
||||
struct policydb *db;
|
||||
// selinux_state does not exists before 4.19
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 337)
|
||||
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||
#ifdef SELINUX_POLICY_INSTEAD_SELINUX_SS
|
||||
struct selinux_policy *policy = rcu_dereference(selinux_state.policy);
|
||||
db = &policy->policydb;
|
||||
@@ -31,7 +31,7 @@ static struct policydb *get_policydb(void)
|
||||
db = &ss->policydb;
|
||||
#endif
|
||||
#else
|
||||
db = &policydb;
|
||||
db = &policydb;
|
||||
#endif
|
||||
return db;
|
||||
}
|
||||
@@ -39,8 +39,7 @@ static struct policydb *get_policydb(void)
|
||||
void apply_kernelsu_rules()
|
||||
{
|
||||
if (!getenforce()) {
|
||||
pr_info("SELinux permissive or disabled, don't apply rules.");
|
||||
return;
|
||||
pr_info("SELinux permissive or disabled, apply rules!");
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
@@ -169,8 +168,9 @@ static int get_object(char *buf, char __user *user_object, size_t buf_sz,
|
||||
}
|
||||
|
||||
// reset avc cache table, otherwise the new rules will not take effect if already denied
|
||||
static void reset_avc_cache() {
|
||||
#if ((KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE) && (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 163))) || (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 337))
|
||||
static void reset_avc_cache()
|
||||
{
|
||||
#ifndef KSU_COMPAT_USE_SELINUX_STATE
|
||||
avc_ss_reset(0);
|
||||
selnl_notify_policyload(0);
|
||||
selinux_status_update_policyload(0);
|
||||
@@ -190,8 +190,7 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
}
|
||||
|
||||
if (!getenforce()) {
|
||||
pr_info("SELinux permissive or disabled, don't apply policies.");
|
||||
return 0;
|
||||
pr_info("SELinux permissive or disabled when handle policy!\n");
|
||||
}
|
||||
|
||||
struct sepol_data data;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "objsec.h"
|
||||
#include "linux/version.h"
|
||||
#include "../klog.h" // IWYU pragma: keep
|
||||
#if ((KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE) && (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 163))) || (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 337))
|
||||
#ifndef KSU_COMPAT_USE_SELINUX_STATE
|
||||
#include "avc.h"
|
||||
#endif
|
||||
|
||||
@@ -57,7 +57,7 @@ if (!is_domain_permissive) {
|
||||
void setenforce(bool enforce)
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 163)) || ((KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 337)))
|
||||
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||
selinux_state.enforcing = enforce;
|
||||
#else
|
||||
selinux_enforcing = enforce;
|
||||
@@ -68,7 +68,7 @@ void setenforce(bool enforce)
|
||||
bool getenforce()
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 163)) || ((KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 337)))
|
||||
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||
if (selinux_state.disabled) {
|
||||
#else
|
||||
if (selinux_disabled) {
|
||||
@@ -78,7 +78,7 @@ bool getenforce()
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 163)) || ((KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 337)))
|
||||
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||
return selinux_state.enforcing;
|
||||
#else
|
||||
return selinux_enforcing;
|
||||
@@ -88,7 +88,7 @@ bool getenforce()
|
||||
#endif
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 337)
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && !defined(KSU_COMPAT_HAS_CURRENT_SID)
|
||||
/*
|
||||
* get the subjective security ID of the current task
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#define __KSU_H_SELINUX
|
||||
|
||||
#include "linux/types.h"
|
||||
#include "linux/version.h"
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || defined(KSU_COMPAT_HAS_SELINUX_STATE)
|
||||
#define KSU_COMPAT_USE_SELINUX_STATE
|
||||
#endif
|
||||
|
||||
void setup_selinux();
|
||||
|
||||
|
||||
@@ -9,6 +9,18 @@
|
||||
|
||||
#define KSU_SUPPORT_ADD_TYPE
|
||||
|
||||
/*
|
||||
* Adapt to Huawei HISI kernel without affecting other kernels ,
|
||||
* Huawei Hisi Kernel EBITMAP Enable or Disable Flag ,
|
||||
* From ss/ebitmap.h
|
||||
*/
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) && \
|
||||
LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
||||
#ifdef HISI_SELINUX_EBITMAP_RO
|
||||
#define CONFIG_IS_HW_HISI
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Declaration
|
||||
//////////////////////////////////////////////////////
|
||||
@@ -453,8 +465,9 @@ static bool add_type_rule(struct policydb *db, const char *s, const char *t,
|
||||
return true;
|
||||
}
|
||||
|
||||
// 5.9.0 : static inline int hashtab_insert(struct hashtab *h, void *key, void *datum, struct hashtab_key_params key_params)
|
||||
// 5.8.0: int hashtab_insert(struct hashtab *h, void *k, void *d);
|
||||
// 5.9.0 : static inline int hashtab_insert(struct hashtab *h, void *key, void
|
||||
// *datum, struct hashtab_key_params key_params) 5.8.0: int
|
||||
// hashtab_insert(struct hashtab *h, void *k, void *d);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||
static u32 filenametr_hash(const void *k)
|
||||
{
|
||||
@@ -486,7 +499,6 @@ static int filenametr_cmp(const void *k1, const void *k2)
|
||||
return v;
|
||||
|
||||
return strcmp(ft1->name, ft2->name);
|
||||
|
||||
}
|
||||
|
||||
static const struct hashtab_key_params filenametr_key_params = {
|
||||
@@ -531,13 +543,13 @@ static bool add_filename_trans(struct policydb *db, const char *s,
|
||||
|
||||
struct filename_trans_datum *last = NULL;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||
struct filename_trans_datum *trans =
|
||||
policydb_filenametr_search(db, &key);
|
||||
#else
|
||||
#else
|
||||
struct filename_trans_datum *trans =
|
||||
hashtab_search(&db->filename_trans, &key);
|
||||
#endif
|
||||
#endif
|
||||
while (trans) {
|
||||
if (ebitmap_get_bit(&trans->stypes, src->value - 1)) {
|
||||
// Duplicate, overwrite existing data and return
|
||||
@@ -551,15 +563,17 @@ static bool add_filename_trans(struct policydb *db, const char *s,
|
||||
}
|
||||
|
||||
if (trans == NULL) {
|
||||
trans = (struct filename_trans_datum*) kcalloc(sizeof(*trans), 1, GFP_ATOMIC);
|
||||
trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
|
||||
1, GFP_ATOMIC);
|
||||
struct filename_trans_key *new_key =
|
||||
(struct filename_trans_key*) kmalloc(sizeof(*new_key), GFP_ATOMIC);
|
||||
(struct filename_trans_key *)kmalloc(sizeof(*new_key),
|
||||
GFP_ATOMIC);
|
||||
*new_key = key;
|
||||
new_key->name = kstrdup(key.name, GFP_ATOMIC);
|
||||
trans->next = last;
|
||||
trans->otype = def->value;
|
||||
hashtab_insert(&db->filename_trans, new_key,
|
||||
trans, filenametr_key_params);
|
||||
hashtab_insert(&db->filename_trans, new_key, trans,
|
||||
filenametr_key_params);
|
||||
}
|
||||
|
||||
db->compat_filename_trans_count++;
|
||||
@@ -575,13 +589,15 @@ static bool add_filename_trans(struct policydb *db, const char *s,
|
||||
hashtab_search(db->filename_trans, &key);
|
||||
|
||||
if (trans == NULL) {
|
||||
trans = (struct filename_trans_datum*) kcalloc(sizeof(*trans), 1, GFP_ATOMIC);
|
||||
trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
|
||||
1, GFP_ATOMIC);
|
||||
if (!trans) {
|
||||
pr_err("add_filename_trans: Failed to alloc datum");
|
||||
return false;
|
||||
}
|
||||
struct filename_trans *new_key =
|
||||
(struct filename_trans*) kmalloc(sizeof(*new_key), GFP_ATOMIC);
|
||||
(struct filename_trans *)kmalloc(sizeof(*new_key),
|
||||
GFP_ATOMIC);
|
||||
if (!new_key) {
|
||||
pr_err("add_filename_trans: Failed to alloc new_key");
|
||||
return false;
|
||||
@@ -592,7 +608,8 @@ static bool add_filename_trans(struct policydb *db, const char *s,
|
||||
hashtab_insert(db->filename_trans, new_key, trans);
|
||||
}
|
||||
|
||||
return ebitmap_set_bit(&db->filename_trans_ttypes, src->value - 1, 1) == 0;
|
||||
return ebitmap_set_bit(&db->filename_trans_ttypes, src->value - 1, 1) ==
|
||||
0;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -679,17 +696,70 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
0);
|
||||
}
|
||||
|
||||
return true;
|
||||
#elif defined(CONFIG_IS_HW_HISI)
|
||||
/*
|
||||
* Huawei use type_attr_map and type_val_to_struct.
|
||||
* And use ebitmap not flex_array.
|
||||
*/
|
||||
size_t new_size = sizeof(struct ebitmap) * db->p_types.nprim;
|
||||
struct ebitmap *new_type_attr_map =
|
||||
(krealloc(db->type_attr_map, new_size, GFP_ATOMIC));
|
||||
|
||||
struct type_datum **new_type_val_to_struct =
|
||||
krealloc(db->type_val_to_struct,
|
||||
sizeof(*db->type_val_to_struct) * db->p_types.nprim,
|
||||
GFP_ATOMIC);
|
||||
|
||||
if (!new_type_attr_map) {
|
||||
pr_err("add_type: alloc type_attr_map failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!new_type_val_to_struct) {
|
||||
pr_err("add_type: alloc type_val_to_struct failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
char **new_val_to_name_types =
|
||||
krealloc(db->sym_val_to_name[SYM_TYPES],
|
||||
sizeof(char *) * db->symtab[SYM_TYPES].nprim,
|
||||
GFP_KERNEL);
|
||||
if (!new_val_to_name_types) {
|
||||
pr_err("add_type: alloc val_to_name failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
db->type_attr_map = new_type_attr_map;
|
||||
ebitmap_init(&db->type_attr_map[value - 1], HISI_SELINUX_EBITMAP_RO);
|
||||
ebitmap_set_bit(&db->type_attr_map[value - 1], value - 1, 1);
|
||||
|
||||
db->type_val_to_struct = new_type_val_to_struct;
|
||||
db->type_val_to_struct[value - 1] = type;
|
||||
|
||||
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
|
||||
db->sym_val_to_name[SYM_TYPES][value - 1] = key;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < db->p_roles.nprim; ++i) {
|
||||
ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
|
||||
0);
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
// flex_array is not extensible, we need to create a new bigger one instead
|
||||
struct flex_array *new_type_attr_map_array = flex_array_alloc(sizeof(struct ebitmap),
|
||||
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO);
|
||||
struct flex_array *new_type_attr_map_array =
|
||||
flex_array_alloc(sizeof(struct ebitmap), db->p_types.nprim,
|
||||
GFP_ATOMIC | __GFP_ZERO);
|
||||
|
||||
struct flex_array *new_type_val_to_struct = flex_array_alloc(sizeof(struct type_datum *),
|
||||
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO);
|
||||
|
||||
struct flex_array *new_val_to_name_types = flex_array_alloc(sizeof(char *),
|
||||
db->symtab[SYM_TYPES].nprim, GFP_ATOMIC | __GFP_ZERO);
|
||||
struct flex_array *new_type_val_to_struct =
|
||||
flex_array_alloc(sizeof(struct type_datum *), db->p_types.nprim,
|
||||
GFP_ATOMIC | __GFP_ZERO);
|
||||
|
||||
struct flex_array *new_val_to_name_types =
|
||||
flex_array_alloc(sizeof(char *), db->symtab[SYM_TYPES].nprim,
|
||||
GFP_ATOMIC | __GFP_ZERO);
|
||||
|
||||
if (!new_type_attr_map_array) {
|
||||
pr_err("add_type: alloc type_attr_map_array failed\n");
|
||||
@@ -707,20 +777,21 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
}
|
||||
|
||||
// preallocate so we don't have to worry about the put ever failing
|
||||
if (flex_array_prealloc(new_type_attr_map_array, 0,
|
||||
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO)) {
|
||||
if (flex_array_prealloc(new_type_attr_map_array, 0, db->p_types.nprim,
|
||||
GFP_ATOMIC | __GFP_ZERO)) {
|
||||
pr_err("add_type: prealloc type_attr_map_array failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flex_array_prealloc(new_type_val_to_struct, 0,
|
||||
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO)) {
|
||||
if (flex_array_prealloc(new_type_val_to_struct, 0, db->p_types.nprim,
|
||||
GFP_ATOMIC | __GFP_ZERO)) {
|
||||
pr_err("add_type: prealloc type_val_to_struct_array failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flex_array_prealloc(new_val_to_name_types, 0,
|
||||
db->symtab[SYM_TYPES].nprim, GFP_ATOMIC | __GFP_ZERO)) {
|
||||
db->symtab[SYM_TYPES].nprim,
|
||||
GFP_ATOMIC | __GFP_ZERO)) {
|
||||
pr_err("add_type: prealloc val_to_name_types failed\n");
|
||||
return false;
|
||||
}
|
||||
@@ -731,25 +802,27 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
for (j = 0; j < db->type_attr_map_array->total_nr_elements; j++) {
|
||||
old_elem = flex_array_get(db->type_attr_map_array, j);
|
||||
if (old_elem)
|
||||
flex_array_put(new_type_attr_map_array, j,
|
||||
old_elem, GFP_ATOMIC | __GFP_ZERO);
|
||||
flex_array_put(new_type_attr_map_array, j, old_elem,
|
||||
GFP_ATOMIC | __GFP_ZERO);
|
||||
}
|
||||
|
||||
for (j = 0; j < db->type_val_to_struct_array->total_nr_elements; j++) {
|
||||
old_elem = flex_array_get_ptr(db->type_val_to_struct_array, j);
|
||||
if (old_elem)
|
||||
flex_array_put_ptr(new_type_val_to_struct, j,
|
||||
old_elem, GFP_ATOMIC | __GFP_ZERO);
|
||||
flex_array_put_ptr(new_type_val_to_struct, j, old_elem,
|
||||
GFP_ATOMIC | __GFP_ZERO);
|
||||
}
|
||||
|
||||
for (j = 0; j < db->symtab[SYM_TYPES].nprim; j++) {
|
||||
old_elem = flex_array_get_ptr(db->sym_val_to_name[SYM_TYPES], j);
|
||||
old_elem =
|
||||
flex_array_get_ptr(db->sym_val_to_name[SYM_TYPES], j);
|
||||
if (old_elem)
|
||||
flex_array_put_ptr(new_val_to_name_types, j,
|
||||
old_elem, GFP_ATOMIC | __GFP_ZERO);
|
||||
flex_array_put_ptr(new_val_to_name_types, j, old_elem,
|
||||
GFP_ATOMIC | __GFP_ZERO);
|
||||
}
|
||||
|
||||
// store the pointer of old flex arrays first, when assigning new ones we should free it
|
||||
// store the pointer of old flex arrays first, when assigning new ones we
|
||||
// should free it
|
||||
struct flex_array *old_fa;
|
||||
|
||||
old_fa = db->type_attr_map_array;
|
||||
@@ -767,16 +840,16 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
if (old_fa) {
|
||||
flex_array_free(old_fa);
|
||||
}
|
||||
flex_array_put_ptr(db->type_val_to_struct_array, value - 1,
|
||||
type, GFP_ATOMIC | __GFP_ZERO);
|
||||
flex_array_put_ptr(db->type_val_to_struct_array, value - 1, type,
|
||||
GFP_ATOMIC | __GFP_ZERO);
|
||||
|
||||
old_fa = db->sym_val_to_name[SYM_TYPES];
|
||||
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
|
||||
if (old_fa) {
|
||||
flex_array_free(old_fa);
|
||||
}
|
||||
flex_array_put_ptr(db->sym_val_to_name[SYM_TYPES], value - 1,
|
||||
key, GFP_ATOMIC | __GFP_ZERO);
|
||||
flex_array_put_ptr(db->sym_val_to_name[SYM_TYPES], value - 1, key,
|
||||
GFP_ATOMIC | __GFP_ZERO);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < db->p_roles.nprim; ++i) {
|
||||
@@ -825,6 +898,12 @@ static void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
|
||||
struct ebitmap *sattr = &db->type_attr_map_array[type->value - 1];
|
||||
#elif defined(CONFIG_IS_HW_HISI)
|
||||
/*
|
||||
* HISI_SELINUX_EBITMAP_RO is Huawei's unique features.
|
||||
*/
|
||||
struct ebitmap *sattr = &db->type_attr_map[type->value - 1],
|
||||
HISI_SELINUX_EBITMAP_RO;
|
||||
#else
|
||||
struct ebitmap *sattr =
|
||||
flex_array_get(db->type_attr_map_array, type->value - 1);
|
||||
|
||||
@@ -17,13 +17,28 @@ fi
|
||||
|
||||
test -d "$GKI_ROOT/KernelSU" || git clone https://github.com/tiann/KernelSU
|
||||
cd "$GKI_ROOT/KernelSU"
|
||||
git stash && git pull
|
||||
git stash
|
||||
if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then
|
||||
git checkout main
|
||||
fi
|
||||
git pull
|
||||
if [ -z "${1-}" ]; then
|
||||
git checkout "$(git describe --abbrev=0 --tags)"
|
||||
else
|
||||
git checkout "$1"
|
||||
fi
|
||||
cd "$GKI_ROOT"
|
||||
|
||||
echo "[+] GKI_ROOT: $GKI_ROOT"
|
||||
echo "[+] Copy kernel su driver to $DRIVER_DIR"
|
||||
|
||||
test -e "$DRIVER_DIR/kernelsu" || ln -sf "$GKI_ROOT/KernelSU/kernel" "$DRIVER_DIR/kernelsu"
|
||||
cd "$DRIVER_DIR"
|
||||
if test -d "$GKI_ROOT/common/drivers"; then
|
||||
ln -sf "../../KernelSU/kernel" "kernelsu"
|
||||
elif test -d "$GKI_ROOT/drivers"; then
|
||||
ln -sf "../KernelSU/kernel" "kernelsu"
|
||||
fi
|
||||
cd "$GKI_ROOT"
|
||||
|
||||
echo '[+] Add kernel su driver to Makefile'
|
||||
|
||||
|
||||
@@ -97,7 +97,10 @@ static void do_update_uid(struct work_struct *work)
|
||||
// first, check if manager_uid exist!
|
||||
bool manager_exist = false;
|
||||
list_for_each_entry (np, &uid_list, list) {
|
||||
if (np->uid == ksu_get_manager_uid()) {
|
||||
// if manager is installed in work profile, the uid in packages.list is still equals main profile
|
||||
// don't delete it in this case!
|
||||
int manager_uid = ksu_get_manager_uid() % 100000;
|
||||
if (np->uid == manager_uid) {
|
||||
manager_exist = true;
|
||||
break;
|
||||
}
|
||||
|
||||
18
manager/.gitignore
vendored
18
manager/.gitignore
vendored
@@ -1,17 +1,9 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
sign.properties
|
||||
.idea
|
||||
.DS_Store
|
||||
build
|
||||
captures
|
||||
.cxx
|
||||
key.jks
|
||||
|
||||
3
manager/.idea/.gitignore
generated
vendored
3
manager/.idea/.gitignore
generated
vendored
@@ -1,3 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
1
manager/.idea/.name
generated
1
manager/.idea/.name
generated
@@ -1 +0,0 @@
|
||||
KernelSU
|
||||
6
manager/.idea/compiler.xml
generated
6
manager/.idea/compiler.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="11" />
|
||||
</component>
|
||||
</project>
|
||||
20
manager/.idea/gradle.xml
generated
20
manager/.idea/gradle.xml
generated
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="Embedded JDK" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
37
manager/.idea/inspectionProfiles/Project_Default.xml
generated
37
manager/.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,37 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
10
manager/.idea/misc.xml
generated
10
manager/.idea/misc.xml
generated
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
||||
6
manager/.idea/vcs.xml
generated
6
manager/.idea/vcs.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,37 +1,51 @@
|
||||
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("com.google.devtools.ksp")
|
||||
kotlin("android")
|
||||
alias(libs.plugins.agp.app)
|
||||
alias(libs.plugins.kotlin)
|
||||
alias(libs.plugins.ksp)
|
||||
alias(libs.plugins.lsplugin.apksign)
|
||||
}
|
||||
|
||||
val managerVersionCode: Int by rootProject.extra
|
||||
val managerVersionName: String by rootProject.extra
|
||||
|
||||
apksign {
|
||||
storeFileProperty = "KEYSTORE_FILE"
|
||||
storePasswordProperty = "KEYSTORE_PASSWORD"
|
||||
keyAliasProperty = "KEY_ALIAS"
|
||||
keyPasswordProperty = "KEY_PASSWORD"
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "me.weishu.kernelsu"
|
||||
|
||||
ndkVersion = "25.1.8937393"
|
||||
|
||||
defaultConfig {
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
ndk {
|
||||
abiFilters += listOf("arm64-v8a", "x86_64")
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
}
|
||||
}
|
||||
|
||||
lint {
|
||||
checkReleaseBuilds = false
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
aidl = true
|
||||
buildConfig = true
|
||||
compose = true
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.3.2"
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.4.3"
|
||||
}
|
||||
|
||||
packaging {
|
||||
jniLibs {
|
||||
useLegacyPackaging = true
|
||||
}
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
@@ -39,15 +53,14 @@ android {
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path(file("src/main/cpp/CMakeLists.txt"))
|
||||
version = "3.18.1"
|
||||
path("src/main/cpp/CMakeLists.txt")
|
||||
}
|
||||
}
|
||||
|
||||
applicationVariants.all {
|
||||
outputs.forEach {
|
||||
val output = it as BaseVariantOutputImpl
|
||||
output.outputFileName = "KernelSU_$versionName-${buildType.name}.apk"
|
||||
output.outputFileName = "KernelSU_${managerVersionName}_${managerVersionCode}-$name.apk"
|
||||
}
|
||||
|
||||
kotlin.sourceSets {
|
||||
@@ -59,34 +72,40 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val accompanistVersion = "0.28.0"
|
||||
val composeDestinationsVersion = "1.7.27-beta"
|
||||
implementation(platform("androidx.compose:compose-bom:2022.12.00"))
|
||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||
implementation("androidx.activity:activity-compose:1.6.1")
|
||||
implementation("androidx.compose.material:material:1.4.0-beta02")
|
||||
implementation("androidx.compose.material:material-icons-extended")
|
||||
implementation("androidx.compose.material3:material3")
|
||||
implementation("androidx.compose.ui:ui")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||
implementation("androidx.core:core-ktx:1.9.0")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1")
|
||||
implementation("androidx.navigation:navigation-compose:2.5.3")
|
||||
implementation("com.google.accompanist:accompanist-drawablepainter:$accompanistVersion")
|
||||
implementation("com.google.accompanist:accompanist-navigation-animation:$accompanistVersion")
|
||||
implementation("com.google.accompanist:accompanist-systemuicontroller:$accompanistVersion")
|
||||
implementation("io.github.raamcosta.compose-destinations:animations-core:$composeDestinationsVersion")
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
|
||||
implementation("io.coil-kt:coil-compose:2.2.2")
|
||||
implementation("me.zhanghai.android.appiconloader:appiconloader-coil:1.5.0")
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.compose.material.icons.extended)
|
||||
implementation(libs.androidx.compose.material)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
implementation(libs.androidx.compose.ui)
|
||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||
|
||||
implementation("com.github.topjohnwu.libsu:core:5.0.3")
|
||||
implementation("com.github.alorma:compose-settings-ui-m3:0.22.0")
|
||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||
|
||||
ksp("io.github.raamcosta.compose-destinations:ksp:$composeDestinationsVersion")
|
||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.4")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0")
|
||||
implementation(libs.com.google.accompanist.drawablepainter)
|
||||
implementation(libs.com.google.accompanist.navigation.animation)
|
||||
implementation(libs.com.google.accompanist.systemuicontroller)
|
||||
|
||||
implementation(libs.compose.destinations.animations.core)
|
||||
ksp(libs.compose.destinations.ksp)
|
||||
|
||||
implementation(libs.com.github.alorma.compose.settings.ui.m3)
|
||||
|
||||
implementation(libs.com.github.topjohnwu.libsu.core)
|
||||
implementation(libs.com.github.topjohnwu.libsu.service)
|
||||
|
||||
implementation(libs.dev.rikka.rikkax.parcelablelist)
|
||||
|
||||
implementation(libs.io.coil.kt.coil.compose)
|
||||
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
|
||||
implementation(libs.me.zhanghai.android.appiconloader.coil)
|
||||
}
|
||||
|
||||
30
manager/app/proguard-rules.pro
vendored
30
manager/app/proguard-rules.pro
vendored
@@ -1,21 +1,9 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
-dontwarn org.bouncycastle.jsse.BCSSLParameters
|
||||
-dontwarn org.bouncycastle.jsse.BCSSLSocket
|
||||
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
|
||||
-dontwarn org.conscrypt.Conscrypt$Version
|
||||
-dontwarn org.conscrypt.Conscrypt
|
||||
-dontwarn org.conscrypt.ConscryptHostnameVerifier
|
||||
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
|
||||
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
|
||||
-dontwarn org.openjsse.net.ssl.OpenJSSE
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
package me.weishu.kernelsu
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("me.weishu.kernelsu", appContext.packageName)
|
||||
}
|
||||
}
|
||||
@@ -2,22 +2,17 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||
tools:ignore="QueryAllPackagesPermission" />
|
||||
|
||||
<application
|
||||
android:name=".KernelSUApplication"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
android:extractNativeLibs="true"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.KernelSU"
|
||||
tools:targetApi="31">
|
||||
tools:targetApi="33">
|
||||
<activity
|
||||
android:name=".ui.MainActivity"
|
||||
android:exported="true"
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// IKsuInterface.aidl
|
||||
package me.weishu.kernelsu;
|
||||
|
||||
import android.content.pm.PackageInfo;
|
||||
import rikka.parcelablelist.ParcelableListSlice;
|
||||
|
||||
interface IKsuInterface {
|
||||
ParcelableListSlice<PackageInfo> getPackages(int flags);
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ksu.h"
|
||||
|
||||
@@ -29,7 +30,14 @@ static bool ksuctl(int cmd, void* arg1, void* arg2) {
|
||||
|
||||
bool become_manager(const char* pkg) {
|
||||
char param[128];
|
||||
sprintf(param, "/data/data/%s", pkg);
|
||||
uid_t uid = getuid();
|
||||
uint32_t userId = uid / 100000;
|
||||
if (userId == 0) {
|
||||
sprintf(param, "/data/data/%s", pkg);
|
||||
} else {
|
||||
snprintf(param, sizeof(param), "/data/user/%d/%s", userId, pkg);
|
||||
}
|
||||
|
||||
return ksuctl(CMD_BECOME_MANAGER, param, nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package me.weishu.kernelsu.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.IBinder;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.topjohnwu.superuser.ipc.RootService;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import me.weishu.kernelsu.IKsuInterface;
|
||||
import rikka.parcelablelist.ParcelableListSlice;
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
* @date 2023/4/18.
|
||||
*/
|
||||
|
||||
public class KsuService extends RootService {
|
||||
|
||||
private static final String TAG = "KsuService";
|
||||
|
||||
class Stub extends IKsuInterface.Stub {
|
||||
@Override
|
||||
public ParcelableListSlice<PackageInfo> getPackages(int flags) {
|
||||
List<PackageInfo> list = getInstalledPackagesAll(flags);
|
||||
Log.i(TAG, "getPackages: " + list.size());
|
||||
return new ParcelableListSlice<>(list);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(@NonNull Intent intent) {
|
||||
return new Stub();
|
||||
}
|
||||
|
||||
List<Integer> getUserIds() {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
|
||||
List<UserHandle> userProfiles = um.getUserProfiles();
|
||||
for (UserHandle userProfile : userProfiles) {
|
||||
int userId = userProfile.hashCode();
|
||||
result.add(userProfile.hashCode());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ArrayList<PackageInfo> getInstalledPackagesAll(int flags) {
|
||||
ArrayList<PackageInfo> packages = new ArrayList<>();
|
||||
for (Integer userId : getUserIds()) {
|
||||
Log.i(TAG, "getInstalledPackagesAll: " + userId);
|
||||
packages.addAll(getInstalledPackagesAsUser(flags, userId));
|
||||
}
|
||||
return packages;
|
||||
}
|
||||
|
||||
List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
|
||||
try {
|
||||
PackageManager pm = getPackageManager();
|
||||
Method getInstalledPackagesAsUser = pm.getClass().getDeclaredMethod("getInstalledPackagesAsUser", int.class, int.class);
|
||||
return (List<PackageInfo>) getInstalledPackagesAsUser.invoke(pm, flags, userId);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "err", e);
|
||||
}
|
||||
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
@@ -5,23 +5,29 @@ import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.NavigationBar
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||
import androidx.navigation.NavHostController
|
||||
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
|
||||
import com.ramcosta.composedestinations.DestinationsNavHost
|
||||
import com.ramcosta.composedestinations.navigation.popBackStack
|
||||
import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState
|
||||
import me.weishu.kernelsu.ui.component.rememberDialogHostState
|
||||
import me.weishu.kernelsu.ui.screen.BottomBarDestination
|
||||
import me.weishu.kernelsu.ui.screen.NavGraphs
|
||||
import me.weishu.kernelsu.ui.screen.appCurrentDestinationAsState
|
||||
import me.weishu.kernelsu.ui.screen.destinations.Destination
|
||||
import me.weishu.kernelsu.ui.screen.startAppDestination
|
||||
import me.weishu.kernelsu.ui.theme.KernelSUTheme
|
||||
import me.weishu.kernelsu.ui.util.LocalDialogHost
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
@@ -58,31 +64,26 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
@Composable
|
||||
private fun BottomBar(navController: NavHostController) {
|
||||
val topDestination: Destination = navController.appCurrentDestinationAsState().value
|
||||
?: NavGraphs.root.startAppDestination
|
||||
val bottomBarRoutes = remember {
|
||||
BottomBarDestination.values().map { it.direction.route }
|
||||
}
|
||||
|
||||
NavigationBar(tonalElevation = 8.dp) {
|
||||
BottomBarDestination.values().forEach { destination ->
|
||||
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
|
||||
NavigationBarItem(
|
||||
selected = topDestination.route == destination.direction.route,
|
||||
selected = isCurrentDestOnBackStack,
|
||||
onClick = {
|
||||
val firstRoute = navController.backQueue.reversed().first {
|
||||
it.destination.route in bottomBarRoutes
|
||||
}.destination.route
|
||||
if (isCurrentDestOnBackStack) {
|
||||
navController.popBackStack(destination.direction, false)
|
||||
}
|
||||
|
||||
navController.navigate(destination.direction.route) {
|
||||
popUpTo(navController.graph.findStartDestination().id) {
|
||||
saveState = firstRoute != destination.direction.route
|
||||
popUpTo(NavGraphs.root.route) {
|
||||
saveState = true
|
||||
}
|
||||
launchSingleTop = true
|
||||
restoreState = true
|
||||
}
|
||||
},
|
||||
icon = {
|
||||
if (topDestination.route == destination.direction.route) {
|
||||
if (isCurrentDestOnBackStack) {
|
||||
Icon(destination.iconSelected, stringResource(destination.label))
|
||||
} else {
|
||||
Icon(destination.iconNotSelected, stringResource(destination.label))
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package me.weishu.kernelsu.ui.component
|
||||
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.widget.TextView
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.text.HtmlCompat
|
||||
import com.google.accompanist.drawablepainter.rememberDrawablePainter
|
||||
import me.weishu.kernelsu.BuildConfig
|
||||
import me.weishu.kernelsu.R
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun AboutCard() {
|
||||
ElevatedCard(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(24.dp)
|
||||
) {
|
||||
AboutCardContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AboutDialog(showAboutDialog: MutableState<Boolean>) {
|
||||
if (showAboutDialog.value) {
|
||||
Dialog(onDismissRequest = { showAboutDialog.value = false }) {
|
||||
AboutCard()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AboutCardContent() {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
val drawable = ResourcesCompat.getDrawable(
|
||||
LocalContext.current.resources,
|
||||
R.mipmap.ic_launcher,
|
||||
LocalContext.current.theme
|
||||
)
|
||||
|
||||
Row {
|
||||
Image(
|
||||
painter = rememberDrawablePainter(drawable),
|
||||
contentDescription = "icon",
|
||||
modifier = Modifier.size(40.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
|
||||
Column {
|
||||
|
||||
Text(
|
||||
stringResource(id = R.string.app_name),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
fontSize = 18.sp
|
||||
)
|
||||
Text(
|
||||
BuildConfig.VERSION_NAME,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
HtmlText(
|
||||
html = stringResource(
|
||||
id = R.string.about_source_code,
|
||||
"<b><a href=\"https://github.com/tiann/KernelSU\">GitHub</a></b>",
|
||||
"<b><a href=\"https://t.me/KernelSU\">Telegram</a></b>"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HtmlText(html: String, modifier: Modifier = Modifier) {
|
||||
val contentColor = LocalContentColor.current
|
||||
AndroidView(
|
||||
modifier = modifier,
|
||||
factory = { context ->
|
||||
TextView(context).also {
|
||||
it.movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
},
|
||||
update = {
|
||||
it.text = HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_COMPACT)
|
||||
it.setTextColor(contentColor.toArgb())
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -1,56 +1,113 @@
|
||||
package me.weishu.kernelsu.ui.component
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import kotlinx.coroutines.CancellableContinuation
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import me.weishu.kernelsu.ui.util.LocalDialogHost
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
sealed interface DialogResult {
|
||||
object Confirmed : DialogResult
|
||||
object Dismissed : DialogResult
|
||||
}
|
||||
interface DialogVisuals
|
||||
|
||||
interface DialogVisuals {
|
||||
interface LoadingDialogVisuals : DialogVisuals
|
||||
|
||||
interface PromptDialogVisuals : DialogVisuals {
|
||||
val title: String
|
||||
val content: String
|
||||
}
|
||||
|
||||
interface ConfirmDialogVisuals : PromptDialogVisuals {
|
||||
val confirm: String?
|
||||
val dismiss: String?
|
||||
}
|
||||
|
||||
interface DialogData {
|
||||
|
||||
sealed interface DialogData {
|
||||
val visuals: DialogVisuals
|
||||
}
|
||||
|
||||
fun confirm()
|
||||
|
||||
interface LoadingDialogData : DialogData {
|
||||
override val visuals: LoadingDialogVisuals
|
||||
fun dismiss()
|
||||
}
|
||||
|
||||
interface PromptDialogData : DialogData {
|
||||
override val visuals: PromptDialogVisuals
|
||||
fun dismiss()
|
||||
}
|
||||
|
||||
interface ConfirmDialogData : PromptDialogData {
|
||||
override val visuals: ConfirmDialogVisuals
|
||||
fun confirm()
|
||||
}
|
||||
|
||||
sealed interface ConfirmResult {
|
||||
object Confirmed : ConfirmResult
|
||||
object Canceled : ConfirmResult
|
||||
}
|
||||
|
||||
class DialogHostState {
|
||||
|
||||
private data class DialogVisualsImpl(
|
||||
private object LoadingDialogVisualsImpl : LoadingDialogVisuals
|
||||
|
||||
private data class PromptDialogVisualsImpl(
|
||||
override val title: String,
|
||||
override val content: String
|
||||
) : PromptDialogVisuals
|
||||
|
||||
private data class ConfirmDialogVisualsImpl(
|
||||
override val title: String,
|
||||
override val content: String,
|
||||
override val confirm: String?,
|
||||
override val dismiss: String?
|
||||
) : DialogVisuals
|
||||
) : ConfirmDialogVisuals
|
||||
|
||||
private data class DialogDataImpl(
|
||||
override val visuals: DialogVisuals,
|
||||
val continuation: CancellableContinuation<DialogResult>
|
||||
) : DialogData {
|
||||
private data class LoadingDialogDataImpl(
|
||||
override val visuals: LoadingDialogVisuals,
|
||||
private val continuation: CancellableContinuation<Unit>,
|
||||
) : LoadingDialogData {
|
||||
override fun dismiss() {
|
||||
if (continuation.isActive) continuation.resume(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
private data class PromptDialogDataImpl(
|
||||
override val visuals: PromptDialogVisuals,
|
||||
private val continuation: CancellableContinuation<Unit>,
|
||||
) : PromptDialogData {
|
||||
override fun dismiss() {
|
||||
if (continuation.isActive) continuation.resume(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
private data class ConfirmDialogDataImpl(
|
||||
override val visuals: ConfirmDialogVisuals,
|
||||
private val continuation: CancellableContinuation<ConfirmResult>
|
||||
) : ConfirmDialogData {
|
||||
|
||||
override fun confirm() {
|
||||
if (continuation.isActive) continuation.resume(DialogResult.Confirmed)
|
||||
if (continuation.isActive) continuation.resume(ConfirmResult.Confirmed)
|
||||
}
|
||||
|
||||
override fun dismiss() {
|
||||
if (continuation.isActive) continuation.resume(DialogResult.Dismissed)
|
||||
if (continuation.isActive) continuation.resume(ConfirmResult.Canceled)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,16 +116,58 @@ class DialogHostState {
|
||||
var currentDialogData by mutableStateOf<DialogData?>(null)
|
||||
private set
|
||||
|
||||
suspend fun showDialog(
|
||||
suspend fun showLoading() {
|
||||
try {
|
||||
mutex.withLock {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
currentDialogData = LoadingDialogDataImpl(
|
||||
visuals = LoadingDialogVisualsImpl,
|
||||
continuation = continuation
|
||||
)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
currentDialogData = null
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun <R> withLoading(block: suspend () -> R) = coroutineScope {
|
||||
val showLoading = launch {
|
||||
showLoading()
|
||||
}
|
||||
|
||||
val result = block()
|
||||
|
||||
showLoading.cancel()
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
suspend fun showPrompt(title: String, content: String) {
|
||||
try {
|
||||
mutex.withLock {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
currentDialogData = PromptDialogDataImpl(
|
||||
visuals = PromptDialogVisualsImpl(title, content),
|
||||
continuation = continuation
|
||||
)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
currentDialogData = null
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun showConfirm(
|
||||
title: String,
|
||||
content: String,
|
||||
confirm: String? = null,
|
||||
dismiss: String? = null
|
||||
): DialogResult = mutex.withLock {
|
||||
): ConfirmResult = mutex.withLock {
|
||||
try {
|
||||
return@withLock suspendCancellableCoroutine { continuation ->
|
||||
currentDialogData = DialogDataImpl(
|
||||
visuals = DialogVisualsImpl(title, content, confirm, dismiss),
|
||||
currentDialogData = ConfirmDialogDataImpl(
|
||||
visuals = ConfirmDialogVisualsImpl(title, content, confirm, dismiss),
|
||||
continuation = continuation
|
||||
)
|
||||
}
|
||||
@@ -85,82 +184,85 @@ fun rememberDialogHostState(): DialogHostState {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BaseDialog(
|
||||
state: DialogHostState = LocalDialogHost.current,
|
||||
title: @Composable (String) -> Unit,
|
||||
confirmButton: @Composable (String?, () -> Unit) -> Unit,
|
||||
dismissButton: @Composable (String?, () -> Unit) -> Unit,
|
||||
content: @Composable (String) -> Unit = { Text(text = it) },
|
||||
) {
|
||||
val currentDialogData = state.currentDialogData ?: return
|
||||
val visuals = currentDialogData.visuals
|
||||
AlertDialog(
|
||||
onDismissRequest = {
|
||||
currentDialogData.dismiss()
|
||||
},
|
||||
title = {
|
||||
title(visuals.title)
|
||||
},
|
||||
text = {
|
||||
content(visuals.content)
|
||||
},
|
||||
confirmButton = {
|
||||
confirmButton(visuals.confirm, currentDialogData::confirm)
|
||||
},
|
||||
dismissButton = {
|
||||
dismissButton(visuals.dismiss, currentDialogData::dismiss)
|
||||
}
|
||||
)
|
||||
private inline fun <reified T : DialogData> DialogData?.tryInto(): T? {
|
||||
return when (this) {
|
||||
is T -> this
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SimpleDialog(
|
||||
fun LoadingDialog(
|
||||
state: DialogHostState = LocalDialogHost.current,
|
||||
content: @Composable (String) -> Unit
|
||||
) {
|
||||
BaseDialog(
|
||||
state = state,
|
||||
state.currentDialogData.tryInto<LoadingDialogData>() ?: return
|
||||
val dialogProperties = remember {
|
||||
DialogProperties(dismissOnClickOutside = false, dismissOnBackPress = false)
|
||||
}
|
||||
Dialog(onDismissRequest = {}, properties = dialogProperties) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.size(100.dp),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PromptDialog(
|
||||
state: DialogHostState = LocalDialogHost.current,
|
||||
) {
|
||||
val promptDialogData = state.currentDialogData.tryInto<PromptDialogData>() ?: return
|
||||
|
||||
val visuals = promptDialogData.visuals
|
||||
AlertDialog(
|
||||
onDismissRequest = {
|
||||
promptDialogData.dismiss()
|
||||
},
|
||||
title = {
|
||||
Text(text = it)
|
||||
Text(text = visuals.title)
|
||||
},
|
||||
confirmButton = { text, confirm ->
|
||||
text?.let {
|
||||
TextButton(onClick = confirm) {
|
||||
Text(text = it)
|
||||
}
|
||||
text = {
|
||||
Text(text = visuals.content)
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = { promptDialogData.dismiss() }) {
|
||||
Text(text = stringResource(id = android.R.string.ok))
|
||||
}
|
||||
},
|
||||
dismissButton = { text, dismiss ->
|
||||
text?.let {
|
||||
TextButton(onClick = dismiss) {
|
||||
Text(text = it)
|
||||
}
|
||||
}
|
||||
},
|
||||
content = content
|
||||
dismissButton = null,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ConfirmDialog(state: DialogHostState = LocalDialogHost.current) {
|
||||
BaseDialog(
|
||||
state = state,
|
||||
title = {
|
||||
Text(text = it)
|
||||
val confirmDialogData = state.currentDialogData.tryInto<ConfirmDialogData>() ?: return
|
||||
|
||||
val visuals = confirmDialogData.visuals
|
||||
AlertDialog(
|
||||
onDismissRequest = {
|
||||
confirmDialogData.dismiss()
|
||||
},
|
||||
confirmButton = { text, confirm ->
|
||||
text?.let {
|
||||
TextButton(onClick = confirm) {
|
||||
Text(text = it)
|
||||
}
|
||||
title = {
|
||||
Text(text = visuals.title)
|
||||
},
|
||||
text = {
|
||||
Text(text = visuals.content)
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = { confirmDialogData.confirm() }) {
|
||||
Text(text = visuals.confirm ?: stringResource(id = android.R.string.ok))
|
||||
}
|
||||
},
|
||||
dismissButton = { text, dismiss ->
|
||||
text?.let {
|
||||
TextButton(onClick = dismiss) {
|
||||
Text(text = it)
|
||||
}
|
||||
dismissButton = {
|
||||
TextButton(onClick = { confirmDialogData.dismiss() }) {
|
||||
Text(text = visuals.dismiss ?: stringResource(id = android.R.string.cancel))
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.PowerManager
|
||||
@@ -30,14 +28,10 @@ import androidx.compose.ui.unit.dp
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootNavGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
import me.weishu.kernelsu.*
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.screen.destinations.SettingScreenDestination
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.util.reboot
|
||||
import me.weishu.kernelsu.ui.util.getSELinuxStatus
|
||||
import me.weishu.kernelsu.ui.util.install
|
||||
import me.weishu.kernelsu.ui.util.*
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@RootNavGraph(start = true)
|
||||
@@ -67,6 +61,8 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
||||
|
||||
StatusCard(kernelVersion, ksuVersion)
|
||||
InfoCard()
|
||||
DonateCard()
|
||||
LearnMoreCard()
|
||||
Spacer(Modifier)
|
||||
}
|
||||
}
|
||||
@@ -102,7 +98,8 @@ private fun TopBar(onSettingsClick: () -> Unit) {
|
||||
|
||||
RebootDropdownItem(id = R.string.reboot)
|
||||
|
||||
val pm = LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
|
||||
val pm =
|
||||
LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) {
|
||||
RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace")
|
||||
}
|
||||
@@ -161,6 +158,16 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
|
||||
text = stringResource(R.string.home_working_version, ksuVersion),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.home_superuser_count, getSuperuserCount()),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.home_module_count, getModuleCount()),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
kernelVersion.isGKI() -> {
|
||||
@@ -197,11 +204,70 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LearnMoreCard() {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val url = stringResource(R.string.home_learn_kernelsu_url)
|
||||
|
||||
ElevatedCard {
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
uriHandler.openUri(url)
|
||||
}
|
||||
.padding(24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column() {
|
||||
Text(
|
||||
text = stringResource(R.string.home_learn_kernelsu),
|
||||
style = MaterialTheme.typography.titleSmall
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.home_click_to_learn_kernelsu),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DonateCard() {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
ElevatedCard {
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
uriHandler.openUri("https://patreon.com/weishu")
|
||||
}
|
||||
.padding(24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column() {
|
||||
Text(
|
||||
text = stringResource(R.string.home_support_title),
|
||||
style = MaterialTheme.typography.titleSmall
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.home_support_content),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoCard() {
|
||||
val context = LocalContext.current
|
||||
val snackbarHost = LocalSnackbarHost.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
ElevatedCard {
|
||||
Column(
|
||||
@@ -221,39 +287,19 @@ private fun InfoCard() {
|
||||
|
||||
InfoCardItem(stringResource(R.string.home_kernel), uname.release)
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(stringResource(R.string.home_manager_version), getManagerVersion(context))
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
InfoCardItem(stringResource(R.string.home_api), Build.VERSION.SDK_INT.toString())
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
InfoCardItem(stringResource(R.string.home_abi), Build.SUPPORTED_ABIS.joinToString(", "))
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(stringResource(R.string.home_fingerprint), Build.FINGERPRINT)
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
InfoCardItem(stringResource(R.string.home_securitypatch), Build.VERSION.SECURITY_PATCH)
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(stringResource(R.string.home_selinux_status), getSELinuxStatus())
|
||||
|
||||
val copiedMessage = stringResource(R.string.home_copied_to_clipboard)
|
||||
TextButton(
|
||||
modifier = Modifier.align(Alignment.End),
|
||||
onClick = {
|
||||
val cm = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
cm.setPrimaryClip(ClipData.newPlainText("KernelSU", contents.toString()))
|
||||
scope.launch { snackbarHost.showSnackbar(copiedMessage) }
|
||||
},
|
||||
content = { Text(stringResource(android.R.string.copy)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getManagerVersion(context: Context) : String {
|
||||
fun getManagerVersion(context: Context): String {
|
||||
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
|
||||
return "${packageInfo.versionName} (${packageInfo.versionCode})"
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import kotlinx.coroutines.launch
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.ConfirmDialog
|
||||
import me.weishu.kernelsu.ui.component.DialogResult
|
||||
import me.weishu.kernelsu.ui.component.ConfirmResult
|
||||
import me.weishu.kernelsu.ui.screen.destinations.InstallScreenDestination
|
||||
import me.weishu.kernelsu.ui.util.*
|
||||
import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
|
||||
@@ -143,13 +143,13 @@ private fun ModuleList(viewModel: ModuleViewModel, modifier: Modifier = Modifier
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
|
||||
suspend fun onModuleUninstall(module: ModuleViewModel.ModuleInfo) {
|
||||
val dialogResult = dialogHost.showDialog(
|
||||
val confirmResult = dialogHost.showConfirm(
|
||||
moduleStr,
|
||||
content = moduleUninstallConfirm.format(module.name),
|
||||
confirm = uninstall,
|
||||
dismiss = cancel
|
||||
)
|
||||
if (dialogResult != DialogResult.Confirmed) {
|
||||
if (confirmResult != ConfirmResult.Confirmed) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -10,20 +10,20 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.core.content.FileProvider
|
||||
import com.alorma.compose.settings.ui.*
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.weishu.kernelsu.BuildConfig
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.SimpleDialog
|
||||
import me.weishu.kernelsu.ui.util.LinkifyText
|
||||
import me.weishu.kernelsu.ui.component.AboutDialog
|
||||
import me.weishu.kernelsu.ui.component.LoadingDialog
|
||||
import me.weishu.kernelsu.ui.util.LocalDialogHost
|
||||
import me.weishu.kernelsu.ui.util.getBugreportFile
|
||||
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
* @date 2023/1/1.
|
||||
@@ -40,46 +40,57 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
})
|
||||
}
|
||||
) { paddingValues ->
|
||||
LoadingDialog()
|
||||
|
||||
SimpleDialog {
|
||||
SupportCard()
|
||||
}
|
||||
val showAboutDialog = remember { mutableStateOf(false) }
|
||||
AboutDialog(showAboutDialog)
|
||||
|
||||
Column(modifier = Modifier.padding(paddingValues)) {
|
||||
|
||||
val context = LocalContext.current
|
||||
SettingsMenuLink(title = {
|
||||
Text(stringResource(id = R.string.send_log))
|
||||
},
|
||||
val scope = rememberCoroutineScope()
|
||||
val dialogHost = LocalDialogHost.current
|
||||
SettingsMenuLink(
|
||||
title = {
|
||||
Text(stringResource(id = R.string.send_log))
|
||||
},
|
||||
onClick = {
|
||||
val bugreport = getBugreportFile(context)
|
||||
val uri: Uri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.fileprovider", bugreport)
|
||||
scope.launch {
|
||||
val bugreport = dialogHost.withLoading {
|
||||
withContext(Dispatchers.IO) {
|
||||
getBugreportFile(context)
|
||||
}
|
||||
}
|
||||
|
||||
val shareIntent = Intent(Intent.ACTION_SEND)
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, uri)
|
||||
shareIntent.setDataAndType(uri, "application/zip")
|
||||
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
val uri: Uri =
|
||||
FileProvider.getUriForFile(
|
||||
context,
|
||||
"${BuildConfig.APPLICATION_ID}.fileprovider",
|
||||
bugreport
|
||||
)
|
||||
|
||||
context.startActivity(
|
||||
Intent.createChooser(
|
||||
shareIntent,
|
||||
context.getString(R.string.send_log)
|
||||
val shareIntent = Intent(Intent.ACTION_SEND)
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, uri)
|
||||
shareIntent.setDataAndType(uri, "application/zip")
|
||||
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
|
||||
context.startActivity(
|
||||
Intent.createChooser(
|
||||
shareIntent,
|
||||
context.getString(R.string.send_log)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
val about = stringResource(id = R.string.about)
|
||||
val ok = stringResource(id = android.R.string.ok)
|
||||
val scope = rememberCoroutineScope()
|
||||
val dialogHost = LocalDialogHost.current
|
||||
SettingsMenuLink(title = {
|
||||
Text(about)
|
||||
},
|
||||
SettingsMenuLink(
|
||||
title = {
|
||||
Text(about)
|
||||
},
|
||||
onClick = {
|
||||
scope.launch {
|
||||
dialogHost.showDialog(about, content = "unused", confirm = ok)
|
||||
}
|
||||
showAboutDialog.value = true
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -97,20 +108,4 @@ private fun TopBar(onBack: () -> Unit = {}) {
|
||||
) { Icon(Icons.Filled.ArrowBack, contentDescription = null) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun SupportCard() {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.bodyMedium) {
|
||||
LinkifyText("Author: weishu")
|
||||
LinkifyText("Github: https://github.com/tiann/KernelSU")
|
||||
LinkifyText("Telegram: https://t.me/KernelSU")
|
||||
LinkifyText("QQ: https://pd.qq.com/s/8lipl1brp")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,10 @@ import com.ramcosta.composedestinations.annotation.Destination
|
||||
import kotlinx.coroutines.launch
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.ConfirmDialog
|
||||
import me.weishu.kernelsu.ui.component.ConfirmResult
|
||||
import me.weishu.kernelsu.ui.component.SearchAppBar
|
||||
import me.weishu.kernelsu.ui.util.LocalDialogHost
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
|
||||
import java.util.*
|
||||
@@ -91,6 +94,8 @@ fun SuperUserScreen() {
|
||||
}
|
||||
) { innerPadding ->
|
||||
|
||||
ConfirmDialog()
|
||||
|
||||
val refreshState = rememberPullRefreshState(
|
||||
refreshing = viewModel.isRefreshing,
|
||||
onRefresh = { scope.launch { viewModel.fetchAppList() } },
|
||||
@@ -103,14 +108,34 @@ fun SuperUserScreen() {
|
||||
val failMessage = stringResource(R.string.superuser_failed_to_grant_root)
|
||||
|
||||
LazyColumn(Modifier.fillMaxSize()) {
|
||||
items(viewModel.appList, key = { it.packageName }) { app ->
|
||||
items(viewModel.appList, key = { it.packageName + it.uid }) { app ->
|
||||
var isChecked by rememberSaveable(app) { mutableStateOf(app.onAllowList) }
|
||||
val dialogHost = LocalDialogHost.current
|
||||
val content =
|
||||
stringResource(id = R.string.superuser_allow_root_confirm, app.label)
|
||||
val confirm = stringResource(id = android.R.string.ok)
|
||||
val cancel = stringResource(id = android.R.string.cancel)
|
||||
|
||||
AppItem(app, isChecked) { checked ->
|
||||
val success = Natives.allowRoot(app.uid, checked)
|
||||
if (success) {
|
||||
isChecked = checked
|
||||
} else scope.launch {
|
||||
snackbarHost.showSnackbar(failMessage.format(app.uid))
|
||||
scope.launch {
|
||||
if (checked) {
|
||||
val confirmResult = dialogHost.showConfirm(
|
||||
app.label,
|
||||
content = content,
|
||||
confirm = confirm,
|
||||
dismiss = cancel
|
||||
)
|
||||
if (confirmResult != ConfirmResult.Confirmed) {
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
|
||||
val success = Natives.allowRoot(app.uid, checked)
|
||||
if (success) {
|
||||
isChecked = checked
|
||||
} else {
|
||||
snackbarHost.showSnackbar(failMessage.format(app.uid))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@ import com.topjohnwu.superuser.CallbackList
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import me.weishu.kernelsu.BuildConfig
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.ksuApp
|
||||
import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
|
||||
import org.json.JSONArray
|
||||
import java.io.File
|
||||
|
||||
|
||||
@@ -21,6 +24,14 @@ private fun getKsuDaemonPath(): String {
|
||||
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksud.so"
|
||||
}
|
||||
|
||||
object KsuCli {
|
||||
val SHELL: Shell = createRootShell()
|
||||
}
|
||||
|
||||
fun getRootShell(): Shell {
|
||||
return KsuCli.SHELL
|
||||
}
|
||||
|
||||
fun createRootShell(): Shell {
|
||||
Shell.enableVerboseLogging = BuildConfig.DEBUG
|
||||
val builder = Shell.Builder.create()
|
||||
@@ -33,7 +44,7 @@ fun createRootShell(): Shell {
|
||||
}
|
||||
|
||||
fun execKsud(args: String): Boolean {
|
||||
val shell = createRootShell()
|
||||
val shell = getRootShell()
|
||||
return ShellUtils.fastCmdResult(shell, "${getKsuDaemonPath()} $args")
|
||||
}
|
||||
|
||||
@@ -44,12 +55,25 @@ fun install() {
|
||||
}
|
||||
|
||||
fun listModules(): String {
|
||||
val shell = createRootShell()
|
||||
val shell = getRootShell()
|
||||
|
||||
val out = shell.newJob().add("${getKsuDaemonPath()} module list").to(ArrayList(), null).exec().out
|
||||
val out =
|
||||
shell.newJob().add("${getKsuDaemonPath()} module list").to(ArrayList(), null).exec().out
|
||||
return out.joinToString("\n").ifBlank { "[]" }
|
||||
}
|
||||
|
||||
fun getModuleCount(): Int {
|
||||
val result = listModules()
|
||||
runCatching {
|
||||
val array = JSONArray(result)
|
||||
return array.length()
|
||||
}.getOrElse { return 0 }
|
||||
}
|
||||
|
||||
fun getSuperuserCount(): Int {
|
||||
return Natives.getAllowList().size
|
||||
}
|
||||
|
||||
fun toggleModule(id: String, enable: Boolean): Boolean {
|
||||
val cmd = if (enable) {
|
||||
"module enable $id"
|
||||
@@ -61,14 +85,14 @@ fun toggleModule(id: String, enable: Boolean): Boolean {
|
||||
return result
|
||||
}
|
||||
|
||||
fun uninstallModule(id: String) : Boolean {
|
||||
fun uninstallModule(id: String): Boolean {
|
||||
val cmd = "module uninstall $id"
|
||||
val result = execKsud(cmd)
|
||||
Log.i(TAG, "uninstall module $id result: $result")
|
||||
return result
|
||||
}
|
||||
|
||||
fun installModule(uri: Uri, onFinish: (Boolean)->Unit, onOutput: (String) -> Unit) : Boolean {
|
||||
fun installModule(uri: Uri, onFinish: (Boolean) -> Unit, onOutput: (String) -> Unit): Boolean {
|
||||
val resolver = ksuApp.contentResolver
|
||||
with(resolver.openInputStream(uri)) {
|
||||
val file = File(ksuApp.cacheDir, "module.zip")
|
||||
@@ -77,7 +101,7 @@ fun installModule(uri: Uri, onFinish: (Boolean)->Unit, onOutput: (String) -> Uni
|
||||
}
|
||||
val cmd = "module install ${file.absolutePath}"
|
||||
|
||||
val shell = createRootShell()
|
||||
val shell = getRootShell()
|
||||
|
||||
val callbackList: CallbackList<String?> = object : CallbackList<String?>() {
|
||||
override fun onAddElement(s: String?) {
|
||||
@@ -85,7 +109,8 @@ fun installModule(uri: Uri, onFinish: (Boolean)->Unit, onOutput: (String) -> Uni
|
||||
}
|
||||
}
|
||||
|
||||
val result = shell.newJob().add("${getKsuDaemonPath()} $cmd").to(callbackList, callbackList).exec()
|
||||
val result =
|
||||
shell.newJob().add("${getKsuDaemonPath()} $cmd").to(callbackList, callbackList).exec()
|
||||
Log.i("KernelSU", "install module $uri result: $result")
|
||||
|
||||
file.delete()
|
||||
@@ -96,7 +121,7 @@ fun installModule(uri: Uri, onFinish: (Boolean)->Unit, onOutput: (String) -> Uni
|
||||
}
|
||||
|
||||
fun reboot(reason: String = "") {
|
||||
val shell = createRootShell()
|
||||
val shell = getRootShell()
|
||||
if (reason == "recovery") {
|
||||
// KEYCODE_POWER = 26, hide incorrect "Factory data reset" message
|
||||
ShellUtils.fastCmd(shell, "/system/bin/input keyevent 26")
|
||||
@@ -105,13 +130,13 @@ fun reboot(reason: String = "") {
|
||||
}
|
||||
|
||||
fun overlayFsAvailable(): Boolean {
|
||||
val shell = createRootShell()
|
||||
val shell = getRootShell()
|
||||
// check /proc/filesystems
|
||||
return ShellUtils.fastCmdResult(shell, "cat /proc/filesystems | grep overlay")
|
||||
}
|
||||
|
||||
fun hasMagisk(): Boolean {
|
||||
val shell = createRootShell()
|
||||
val shell = getRootShell()
|
||||
val result = shell.newJob().add("nsenter --mount=/proc/1/ns/mnt which magisk").exec()
|
||||
Log.i(TAG, "has magisk: ${result.isSuccess}")
|
||||
return result.isSuccess
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.weishu.kernelsu.ui.util
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.system.Os
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.ui.screen.getManagerVersion
|
||||
import java.io.File
|
||||
@@ -22,26 +23,32 @@ fun getBugreportFile(context: Context): File {
|
||||
val dropboxFile = File(bugreportDir, "dropbox.tar.gz")
|
||||
val pstoreFile = File(bugreportDir, "pstore.tar.gz")
|
||||
val diagFile = File(bugreportDir, "diag.tar.gz")
|
||||
val bootlogFile = File(bugreportDir, "bootlog.tar.gz")
|
||||
val mountsFile = File(bugreportDir, "mounts.txt")
|
||||
val fileSystemsFile = File(bugreportDir, "filesystems.txt")
|
||||
val ksuFileTree = File(bugreportDir, "ksu_tree.txt")
|
||||
val appListFile = File(bugreportDir, "app_list.txt")
|
||||
val appListFile = File(bugreportDir, "packages.txt")
|
||||
val propFile = File(bugreportDir, "props.txt")
|
||||
val allowListFile = File(bugreportDir, "allowlist.bin")
|
||||
|
||||
val shell = createRootShell()
|
||||
val shell = getRootShell()
|
||||
|
||||
shell.newJob().add("dmesg > ${dmesgFile.absolutePath}").exec()
|
||||
shell.newJob().add("logcat -d > ${logcatFile.absolutePath}").exec()
|
||||
shell.newJob().add("tar -czf ${tombstonesFile.absolutePath} /data/tombstones").exec()
|
||||
shell.newJob().add("tar -czf ${dropboxFile.absolutePath} /data/system/dropbox").exec()
|
||||
shell.newJob().add("tar -czf ${pstoreFile.absolutePath} /sys/fs/pstore").exec()
|
||||
shell.newJob().add("tar -czf ${diagFile.absolutePath} /data/vendor/diag").exec()
|
||||
shell.newJob().add("tar -czf ${tombstonesFile.absolutePath} -C /data/tombstones .").exec()
|
||||
shell.newJob().add("tar -czf ${dropboxFile.absolutePath} -C /data/system/dropbox .").exec()
|
||||
shell.newJob().add("tar -czf ${pstoreFile.absolutePath} -C /sys/fs/pstore .").exec()
|
||||
shell.newJob().add("tar -czf ${diagFile.absolutePath} -C /data/vendor/diag .").exec()
|
||||
shell.newJob().add("tar -czf ${bootlogFile.absolutePath} -C /data/adb/ksu/log .").exec()
|
||||
|
||||
shell.newJob().add("cat /proc/mounts > ${mountsFile.absolutePath}").exec()
|
||||
shell.newJob().add("cat /proc/1/mountinfo > ${mountsFile.absolutePath}").exec()
|
||||
shell.newJob().add("cat /proc/filesystems > ${fileSystemsFile.absolutePath}").exec()
|
||||
shell.newJob().add("ls -alRZ /data/adb > ${ksuFileTree.absolutePath}").exec()
|
||||
shell.newJob().add("cat /data/system/packages.list > ${appListFile.absolutePath}").exec()
|
||||
shell.newJob().add("cp /data/system/packages.list ${appListFile.absolutePath}").exec()
|
||||
shell.newJob().add("getprop > ${propFile.absolutePath}").exec()
|
||||
shell.newJob().add("cp /data/adb/ksu/.allowlist ${allowListFile.absolutePath}").exec()
|
||||
|
||||
val selinux = ShellUtils.fastCmd(shell, "getenforce");
|
||||
|
||||
// basic information
|
||||
val buildInfo = File(bugreportDir, "basic.txt")
|
||||
@@ -56,6 +63,7 @@ fun getBugreportFile(context: Context): File {
|
||||
pw.println("FINGERPRINT: " + Build.FINGERPRINT)
|
||||
pw.println("DEVICE: " + Build.DEVICE)
|
||||
pw.println("Manager: " + getManagerVersion(context))
|
||||
pw.println("SELinux: $selinux")
|
||||
|
||||
val uname = Os.uname()
|
||||
pw.println("KernelRelease: ${uname.release}")
|
||||
@@ -84,4 +92,4 @@ fun getBugreportFile(context: Context): File {
|
||||
shell.newJob().add("chmod 0644 ${targetFile.absolutePath}").exec()
|
||||
|
||||
return targetFile
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package me.weishu.kernelsu.ui.viewmodel
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageInfo
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.IBinder
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
@@ -10,13 +13,19 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.weishu.kernelsu.IKsuInterface
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.ksuApp
|
||||
import me.weishu.kernelsu.ui.KsuService
|
||||
import me.weishu.kernelsu.ui.util.HanziToPinyin
|
||||
import me.weishu.kernelsu.ui.util.KsuCli
|
||||
import java.text.Collator
|
||||
import java.util.*
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class SuperUserViewModel : ViewModel() {
|
||||
|
||||
@@ -62,16 +71,61 @@ class SuperUserViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
private suspend inline fun connectKsuService(
|
||||
crossinline onDisconnect: () -> Unit = {}
|
||||
): Pair<IBinder, ServiceConnection> = suspendCoroutine {
|
||||
val connection = object : ServiceConnection {
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
onDisconnect()
|
||||
}
|
||||
|
||||
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
|
||||
it.resume(binder as IBinder to this)
|
||||
}
|
||||
}
|
||||
|
||||
val intent = Intent(ksuApp, KsuService::class.java);
|
||||
|
||||
val task = KsuService.bindOrTask(
|
||||
intent,
|
||||
Shell.EXECUTOR,
|
||||
connection,
|
||||
)
|
||||
val shell = KsuCli.SHELL
|
||||
task?.let { it1 -> shell.execTask(it1) }
|
||||
}
|
||||
|
||||
private fun stopKsuService() {
|
||||
val intent = Intent(ksuApp, KsuService::class.java);
|
||||
KsuService.stop(intent)
|
||||
}
|
||||
|
||||
suspend fun fetchAppList() {
|
||||
|
||||
isRefreshing = true
|
||||
|
||||
val result = connectKsuService {
|
||||
Log.w(TAG, "KsuService disconnected")
|
||||
}
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
isRefreshing = true
|
||||
val pm = ksuApp.packageManager
|
||||
val allowList = Natives.getAllowList().toSet()
|
||||
val denyList = Natives.getDenyList().toSet()
|
||||
Log.i(TAG, "allowList: $allowList")
|
||||
Log.i(TAG, "denyList: $denyList")
|
||||
val start = SystemClock.elapsedRealtime()
|
||||
apps = pm.getInstalledPackages(0).map {
|
||||
|
||||
val binder = result.first
|
||||
val allPackages = IKsuInterface.Stub.asInterface(binder).getPackages(0)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
stopKsuService()
|
||||
}
|
||||
|
||||
val packages = allPackages.list
|
||||
|
||||
apps = packages.map {
|
||||
val appInfo = it.applicationInfo
|
||||
val uid = appInfo.uid
|
||||
AppInfo(
|
||||
|
||||
68
manager/app/src/main/res/values-ar/strings.xml
Normal file
68
manager/app/src/main/res/values-ar/strings.xml
Normal file
@@ -0,0 +1,68 @@
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">KernelSU</string>
|
||||
|
||||
<string name="home">الرئيسية</string>
|
||||
<string name="home_not_installed">غير مثبت</string>
|
||||
<string name="home_click_to_install">اضغط لتثبيت</string>
|
||||
<string name="home_working">يعمل</string>
|
||||
<string name="home_working_version">الاصدار: %d</string>
|
||||
<string name="home_superuser_count">مستخدمين الجذر: %d</string>
|
||||
<string name="home_module_count">الوحدات: %d</string>
|
||||
<string name="home_unsupported">غير مدعوم</string>
|
||||
<string name="home_unsupported_reason">KernelSU يدعم GKI kernels الان</string>
|
||||
<string name="home_copied_to_clipboard">نسخ الي حافظة</string>
|
||||
<string name="home_support">مدعوم</string>
|
||||
|
||||
<string name="home_kernel">نواه</string>
|
||||
<string name="home_arch">معمارية</string>
|
||||
<string name="home_manager_version">اصدار المدير</string>
|
||||
<string name="home_api">API Level</string>
|
||||
<string name="home_abi">ABI</string>
|
||||
<string name="home_fingerprint">بصمة</string>
|
||||
<string name="home_securitypatch">تصحيح الأمان</string>
|
||||
|
||||
<string name="home_selinux_status">وضع SELinux</string>
|
||||
<string name="selinux_status_disabled">غير مفعل</string>
|
||||
<string name="selinux_status_enforcing">فرض</string>
|
||||
<string name="selinux_status_permissive">متساهل</string>
|
||||
<string name="selinux_status_unknown">مجهول</string>
|
||||
<string name="superuser">مستخدم الجذر</string>
|
||||
<string name="superuser_failed_to_grant_root">فشل منح الجذر لـ %d</string>
|
||||
<string name="superuser_allow_root_confirm">هل أنت متأكد من منح حق الوصول إلى الجذر إلى %s?</string>
|
||||
<string name="module_failed_to_enable">فشل في تمكين الوحدة النمطية: %s</string>
|
||||
<string name="module_failed_to_disable">فشل تعطيل الوحدة النمطية: %s</string>
|
||||
<string name="module_empty">لا توجد وحدة مثبتة</string>
|
||||
|
||||
<string name="module">وحدة</string>
|
||||
<string name="uninstall">الغاء التثبيت</string>
|
||||
<string name="module_install">تثبيت</string>
|
||||
<string name="install">تثبيت</string>
|
||||
<string name="reboot">اعادة تشغيل</string>
|
||||
<string name="settings">الاعدادات</string>
|
||||
<string name="reboot_userspace">اعادة تشغيل سريع</string>
|
||||
<string name="reboot_recovery">اعادة تشغيل الي ريكفيري</string>
|
||||
<string name="reboot_bootloader">اعادة تشغيل الي بوت لودر</string>
|
||||
<string name="reboot_download">اعادة تشغيل الي وضع داونلود</string>
|
||||
<string name="reboot_edl">اعادة تشغيل الي وضع EDL</string>
|
||||
<string name="about">من نحن</string>
|
||||
<string name="require_kernel_version_8">يتطلب KernelSU اصدار 8+</string>
|
||||
<string name="module_uninstall_confirm">Are you sure you want to uninstall module %s?</string>
|
||||
<string name="module_uninstall_success">%s uninstalled</string>
|
||||
<string name="module_uninstall_failed">Failed to uninstall: %s</string>
|
||||
<string name="module_version">الاصدار</string>
|
||||
<string name="module_author">مؤلف</string>
|
||||
<string name="module_overlay_fs_not_available">التراكبات غير متوفرة ، لا يمكن للوحدة أن تعمل!</string>
|
||||
<string name="refresh">رفريش</string>
|
||||
<string name="show_system_apps">عرض تطبيقات النظام</string>
|
||||
<string name="hide_system_apps">اخفاء تطبيقات النظام</string>
|
||||
<string name="send_log">ارسال اللوج</string>
|
||||
<string name="safe_mode">وضع الامن</string>
|
||||
<string name="reboot_to_apply">اعادة تشغيل لتطبيق التغيرات</string>
|
||||
<string name="module_magisk_conflict">تم تعطيل الوحدات النمطية لأنها تتعارض مع Magisk\'s!</string>
|
||||
<string name="home_learn_kernelsu">تعلم KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">تعرف على كيفية تثبيت KernelSU واستخدام الوحدات النمطية</string>
|
||||
<string name="home_support_title">ادعمنا</string>
|
||||
<string name="home_support_content">KernelSU هو وسيظل دائمًا مجانيًا ومفتوح المصدر. ومع ذلك ، يمكنك أن تظهر لنا أنك تهتم بالتبرع.</string>
|
||||
<string name="about_source_code"><![CDATA[View source code at %1$s<br/>Join our %2$s channel]]></string>
|
||||
</resources>
|
||||
69
manager/app/src/main/res/values-bn/strings.xml
Normal file
69
manager/app/src/main/res/values-bn/strings.xml
Normal file
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">কার্নেলএসইউ</string>
|
||||
|
||||
<string name="home">হোম</string>
|
||||
<string name="home_not_installed">ইনস্টল করা হয়নি</string>
|
||||
<string name="home_click_to_install">ইনস্টল করার জন্য ক্লিক করুন</string>
|
||||
<string name="home_working"> ওয়ার্কিং</string>
|
||||
<string name="home_working_version">ওয়ার্কিং সংস্করণ: %d</string>
|
||||
<string name="home_superuser_count">সুপার ইউজার: %d</string>
|
||||
<string name="home_module_count">মডিউল: %d</string>
|
||||
<string name="home_unsupported">অসমর্থিত</string>
|
||||
<string name="home_unsupported_reason">কার্নেলএসইউ শুধুমাত্র জিকেআই কার্নেল সমর্থন করে</string>
|
||||
<string name="home_copied_to_clipboard">ক্লিপবোর্ডে কপি করা হয়েছে</string>
|
||||
<string name="home_support">সমর্থন</string>
|
||||
|
||||
<string name="home_kernel">কার্নেল</string>
|
||||
<string name="home_arch">খিলান</string>
|
||||
<string name="home_manager_version">ম্যানেজার সংস্করণ</string>
|
||||
<string name="home_api">এপিআই স্তর</string>
|
||||
<string name="home_abi">এবিআই</string>
|
||||
<string name="home_fingerprint">ফিঙ্গারপ্রিন্ট</string>
|
||||
<string name="home_securitypatch">সিকিউরিটি প্যাচ</string>
|
||||
|
||||
<string name="home_selinux_status">সেলিনাক্স স্ট্যাটাস</string>
|
||||
<string name="selinux_status_disabled">ডিজেবল</string>
|
||||
<string name="selinux_status_enforcing">এনফোর্সিং</string>
|
||||
<string name="selinux_status_permissive">অনুমতিমূলক</string>
|
||||
<string name="selinux_status_unknown">অপরিচিত</string>
|
||||
<string name="superuser">সুপার ইউজার</string>
|
||||
<string name="superuser_failed_to_grant_root">এর জন্য রুট প্রদান করতে ব্যর্থ হয়েছে %d</string>
|
||||
<string name="superuser_allow_root_confirm">নিশ্চিত আপনি রুট দিতে চান %s?</string>
|
||||
<string name="module_failed_to_enable">মডিউল সক্ষম করতে ব্যর্থ হয়েছে: %s</string>
|
||||
<string name="module_failed_to_disable">মডিউল নিষ্ক্রিয় করতে ব্যর্থ হয়েছে: %s</string>
|
||||
<string name="module_empty">কোন মডিউল ইনস্টল করা নেই</string>
|
||||
|
||||
<string name="module">মডিউল</string>
|
||||
<string name="uninstall">আনইন্সটল</string>
|
||||
<string name="module_install">মডিউল ইনস্টল</string>
|
||||
<string name="install">ইনস্টল</string>
|
||||
<string name="reboot">রিবুট</string>
|
||||
<string name="settings">সেটিংস</string>
|
||||
<string name="reboot_userspace">সফট রিবুট</string>
|
||||
<string name="reboot_recovery">রিবুট রিকোভারি</string>
|
||||
<string name="reboot_bootloader">রিবুট বুটলোডার</string>
|
||||
<string name="reboot_download">রিবুট ডাউনলোড</string>
|
||||
<string name="reboot_edl">রিবুট ইডিএল</string>
|
||||
<string name="about">এবাউট</string>
|
||||
<string name="require_kernel_version_8">কার্নেলএসইউ সংস্করণ প্রয়োজন ৮+</string>
|
||||
<string name="module_uninstall_confirm">মডিউল আনইনস্টল নিশ্চিত করুন %s?</string>
|
||||
<string name="module_uninstall_success">%s আনইনস্টল সফল</string>
|
||||
<string name="module_uninstall_failed">আনইন্সটল ব্যর্থ: %s</string>
|
||||
<string name="module_version">ভার্সন</string>
|
||||
<string name="module_author">লেখক</string>
|
||||
<string name="module_overlay_fs_not_available">ওভারলেএফএস উপলব্ধ নয়, মডিউল কাজ করতে পারে না!</string>
|
||||
<string name="refresh">রিফ্রেশ</string>
|
||||
<string name="show_system_apps">শো সিস্টেম অ্যাপস</string>
|
||||
<string name="hide_system_apps">হাইড সিস্টেম অ্যাপস</string>
|
||||
<string name="send_log">সেন্ড লগ</string>
|
||||
<string name="safe_mode">সেইফ মোড</string>
|
||||
<string name="reboot_to_apply">রিবুট এপ্লাই</string>
|
||||
<string name="module_magisk_conflict">মডিউলগুলি অক্ষম কারণ তারা ম্যাজিস্কের সাথে বিরোধিতা করে!</string>
|
||||
<string name="home_learn_kernelsu">লার্ন কার্নেলএসইউ</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">কিভাবে কার্নেলএসইউ ইনস্টল করতে হয় এবং মডিউল ব্যবহার করতে হয় তা শিখুন</string>
|
||||
<string name="home_support_title">সাপোর্ট টাইটেল</string>
|
||||
<string name="home_support_content">কার্নেলএসইউ বিনামূল্যে এবং ওপেন সোর্স, এবং সবসময় থাকবে। আপনি সবসময় একটি অনুদান দিয়ে আপনার কৃতজ্ঞতা প্রদর্শন করতে পারেন.</string>
|
||||
<string name="about_source_code"><![CDATA[Bekijk source code op %1$s<br/>আমাদের %2$s চ্যানেল মার্জ করুন]]></string>
|
||||
</resources>
|
||||
@@ -5,6 +5,8 @@
|
||||
<string name="home_click_to_install">Klik untuk menginstall</string>
|
||||
<string name="home_working">Bekerja</string>
|
||||
<string name="home_working_version">Versi: %d</string>
|
||||
<string name="home_superuser_count">Superuser: %d</string>
|
||||
<string name="home_module_count">Module: %d</string>
|
||||
<string name="home_unsupported">Tidak didukung</string>
|
||||
<string name="home_unsupported_reason">Saat ini kernelSu hanya mendukung GKI kernel</string>
|
||||
<string name="home_copied_to_clipboard">Salin ke clipboard</string>
|
||||
@@ -23,13 +25,14 @@
|
||||
<string name="selinux_status_unknown">Tidak tersedia</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="superuser_failed_to_grant_root">Gagal mengizinkan root untuk %d</string>
|
||||
<string name="superuser_allow_root_confirm">Ijinkan akses root untuk aplikasi %s?</string>
|
||||
<string name="module_failed_to_enable">Gagal mengaktifkan module: %s</string>
|
||||
<string name="module_failed_to_disable">Gagal menonaktifkan module: %s</string>
|
||||
<string name="module_empty">Tidak ada module terpasang</string>
|
||||
<string name="module">Module</string>
|
||||
<string name="uninstall">Hapus</string>
|
||||
<string name="module_install">Memasang module</string>
|
||||
<string name="install">Pasang</string>
|
||||
<string name="module_install">Pasang module</string>
|
||||
<string name="install">Memasang module...</string>
|
||||
<string name="reboot">Reboot perangkat</string>
|
||||
<string name="settings">Pengaturan</string>
|
||||
<string name="reboot_userspace">Soft Reboot</string>
|
||||
@@ -52,4 +55,10 @@
|
||||
<string name="safe_mode">Mode aman</string>
|
||||
<string name="reboot_to_apply">Restart untuk menerapkan</string>
|
||||
<string name="module_magisk_conflict">Module akan di nonaktifkan karna konflik dengan magisk\'s!</string>
|
||||
<string name="home_learn_kernelsu">Belajar KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/id_ID/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">Cara menginstall KernelSu dan menggunakan Module</string>
|
||||
<string name="home_support_title">Dukung kami</string>
|
||||
<string name="home_support_content">KernelSU akan selalu gratis dan open source. Namun Anda dapat menunjukkan kepada kami bahwa Anda peduli dengan memberikan sedikit donasi.</string>
|
||||
<string name="about_source_code"><![CDATA[Lihat sumber kode di %1$s<br/>Gabung sekarang %2$s channel]]></string>
|
||||
</resources>
|
||||
|
||||
66
manager/app/src/main/res/values-it/strings.xml
Normal file
66
manager/app/src/main/res/values-it/strings.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<resources>
|
||||
<string name="home">Home</string>
|
||||
<string name="home_not_installed">Non installato</string>
|
||||
<string name="home_click_to_install">Clicca per installare</string>
|
||||
<string name="home_working">In esecuzione</string>
|
||||
<string name="home_working_version">Versione: %d</string>
|
||||
<string name="home_superuser_count">Superuser: %d</string>
|
||||
<string name="home_module_count">Moduli: %d</string>
|
||||
<string name="home_unsupported">Non supportato</string>
|
||||
<string name="home_unsupported_reason">KernelSU ora supporta solo i kernel GKI</string>
|
||||
<string name="home_copied_to_clipboard">Copiato negli appunti</string>
|
||||
<string name="home_support">Supporto</string>
|
||||
|
||||
<string name="home_kernel">Kernel</string>
|
||||
<string name="home_arch">Architettura</string>
|
||||
<string name="home_manager_version">Versione del manager</string>
|
||||
<string name="home_api">Livello API</string>
|
||||
<string name="home_abi">ABI</string>
|
||||
<string name="home_fingerprint">Fingerprint</string>
|
||||
<string name="home_securitypatch">Patch di sicurezza</string>
|
||||
|
||||
<string name="home_selinux_status">Stato SELinux</string>
|
||||
<string name="selinux_status_disabled">Disabilitato</string>
|
||||
<string name="selinux_status_enforcing">Enforcing</string>
|
||||
<string name="selinux_status_permissive">Permissive</string>
|
||||
<string name="selinux_status_unknown">Sconosciuto</string>
|
||||
<string name="superuser">Accesso root</string>
|
||||
<string name="superuser_failed_to_grant_root">Impossibile concedere l\'accesso root per %d</string>
|
||||
<string name="superuser_allow_root_confirm">Sei sicuro di concedere l\'accesso root a %s?</string>
|
||||
<string name="module_failed_to_enable">Impossibile abilitare il modulo: %s</string>
|
||||
<string name="module_failed_to_disable">Impossibile disabilitare il modulo: %s</string>
|
||||
<string name="module_empty">Nessun modulo installato</string>
|
||||
|
||||
<string name="module">Moduli</string>
|
||||
<string name="uninstall">Disinstalla</string>
|
||||
<string name="module_install">Installa</string>
|
||||
<string name="install">Installa</string>
|
||||
<string name="reboot">Riavvia</string>
|
||||
<string name="settings">Impostazioni</string>
|
||||
<string name="reboot_userspace">Riavvio veloce</string>
|
||||
<string name="reboot_recovery">Riavvia in Recovery</string>
|
||||
<string name="reboot_bootloader">Riavvia in modalità Bootloader</string>
|
||||
<string name="reboot_download">Riavvia in modalità Download</string>
|
||||
<string name="reboot_edl">Riavvia in modalità EDL</string>
|
||||
<string name="about">Informazioni</string>
|
||||
<string name="require_kernel_version_8">Richiede KernelSU versione 8+</string>
|
||||
<string name="module_uninstall_confirm">Sei sicuro di voler disinstallare il modulo %s?</string>
|
||||
<string name="module_uninstall_success">%s disinstallato</string>
|
||||
<string name="module_uninstall_failed">Impossibile disinstallare: %s</string>
|
||||
<string name="module_version">Versione</string>
|
||||
<string name="module_author">Autore</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs non è disponibile, il modulo non può funzionare!</string>
|
||||
<string name="refresh">Aggiorna</string>
|
||||
<string name="show_system_apps">Mostra app di sistema</string>
|
||||
<string name="hide_system_apps">Nascondi app di sistema</string>
|
||||
<string name="send_log">Invia log</string>
|
||||
<string name="safe_mode">Modalità provvisoria</string>
|
||||
<string name="reboot_to_apply">Riavvia per applicare la modifica</string>
|
||||
<string name="module_magisk_conflict">I moduli sono disabilitati perché in conflitto con quelli di Magisk!</string>
|
||||
<string name="home_learn_kernelsu">Scopri KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">Scopri come installare KernelSU e utilizzare i moduli</string>
|
||||
<string name="home_support_title">Supportaci</string>
|
||||
<string name="home_support_content">KernelSU è, e sempre sarà, gratuito e open source. Puoi comunque mostrarci il tuo apprezzamento facendo una donazione.</string>
|
||||
<string name="about_source_code"><![CDATA[Visualizza il codice sorgente su %1$s<br/>Unisciti al nostro canale %2$s]]></string>
|
||||
</resources>
|
||||
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">KernelSU</string>
|
||||
<string name="home">ホーム</string>
|
||||
<string name="home">ホーム</string>
|
||||
<string name="home_not_installed">未インストール</string>
|
||||
<string name="home_click_to_install">タップでインストール</string>
|
||||
<string name="home_working">動作中</string>
|
||||
@@ -40,6 +39,7 @@
|
||||
<string name="reboot_edl">EDLで再起動</string>
|
||||
<string name="about">アプリについて</string>
|
||||
<string name="require_kernel_version_8">KernelSU バージョン8以降が必要です</string>
|
||||
<string name="module_uninstall_confirm">モジュール %s をアンインストールしますか?</string>
|
||||
<string name="module_uninstall_success">%sをアンインストールしました</string>
|
||||
<string name="module_uninstall_failed">アンインストールに失敗: %s</string>
|
||||
<string name="module_version">バージョン</string>
|
||||
@@ -51,4 +51,5 @@
|
||||
<string name="send_log">ログを送信</string>
|
||||
<string name="safe_mode">セーフモード</string>
|
||||
<string name="reboot_to_apply">再起動をして有効化する</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
</resources>
|
||||
|
||||
64
manager/app/src/main/res/values-ko/strings.xml
Normal file
64
manager/app/src/main/res/values-ko/strings.xml
Normal file
@@ -0,0 +1,64 @@
|
||||
<resources>
|
||||
<string name="home">홈</string>
|
||||
<string name="home_not_installed">설치되지 않음</string>
|
||||
<string name="home_click_to_install">이 곳을 눌러 설치하기</string>
|
||||
<string name="home_working">정상 작동 중</string>
|
||||
<string name="home_working_version">버전: %d</string>
|
||||
<string name="home_superuser_count">루트 권한: %d개</string>
|
||||
<string name="home_module_count">설치된 모듈: %d개</string>
|
||||
<string name="home_unsupported">지원되지 않음</string>
|
||||
<string name="home_unsupported_reason">KernelSU는 현재 GKI 커널만 지원합니다</string>
|
||||
<string name="home_copied_to_clipboard">클립보드로 복사됨</string>
|
||||
<string name="home_support">지원</string>
|
||||
|
||||
<string name="home_kernel">커널</string>
|
||||
<string name="home_arch">아키텍처</string>
|
||||
<string name="home_manager_version">매니저 버전</string>
|
||||
<string name="home_api">API 레벨</string>
|
||||
<string name="home_fingerprint">빌드 정보</string>
|
||||
<string name="home_securitypatch">보안 패치 버전</string>
|
||||
|
||||
<string name="home_selinux_status">SELinux 상태</string>
|
||||
<string name="selinux_status_disabled">비활성화됨</string>
|
||||
<string name="selinux_status_enforcing">적용</string>
|
||||
<string name="selinux_status_permissive">허용</string>
|
||||
<string name="selinux_status_unknown">알 수 없음</string>
|
||||
<string name="superuser">슈퍼유저</string>
|
||||
<string name="superuser_failed_to_grant_root">%d에 루트 권한 부여 실패</string>
|
||||
<string name="superuser_allow_root_confirm">%s에 루트 권한을 부여할까요?</string>
|
||||
<string name="module_failed_to_enable">모듈 활성화 실패: %s</string>
|
||||
<string name="module_failed_to_disable">모듈 비활성화 실패: %s</string>
|
||||
<string name="module_empty">설치된 모듈 없음</string>
|
||||
|
||||
<string name="module">모듈</string>
|
||||
<string name="uninstall">삭제</string>
|
||||
<string name="module_install">설치</string>
|
||||
<string name="install">설치</string>
|
||||
<string name="reboot">다시 시작</string>
|
||||
<string name="settings">설정</string>
|
||||
<string name="reboot_userspace">빠른 다시 시작</string>
|
||||
<string name="reboot_recovery">복구 모드로 다시 시작</string>
|
||||
<string name="reboot_bootloader">부트로더로 다시 시작</string>
|
||||
<string name="reboot_download">다운로드 모드로 다시 시작</string>
|
||||
<string name="reboot_edl">EDL 모드로 다시 시작</string>
|
||||
<string name="about">정보</string>
|
||||
<string name="require_kernel_version_8">버전 8 이상의 KernelSU가 필요합니다</string>
|
||||
<string name="module_uninstall_confirm">%s 모듈을 삭제할까요?</string>
|
||||
<string name="module_uninstall_success">%s 모듈 삭제됨</string>
|
||||
<string name="module_uninstall_failed">모듈 삭제 실패: %s</string>
|
||||
<string name="module_version">버전</string>
|
||||
<string name="module_author">제작자</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs 사용 불가, 모듈을 사용할 수 없습니다!</string>
|
||||
<string name="refresh">새로고침</string>
|
||||
<string name="show_system_apps">시스템 앱 보이기</string>
|
||||
<string name="hide_system_apps">시스템 앱 숨기기</string>
|
||||
<string name="send_log">로그 보내기</string>
|
||||
<string name="safe_mode">안전 모드</string>
|
||||
<string name="reboot_to_apply">다시 시작하여 변경 사항 적용</string>
|
||||
<string name="module_magisk_conflict">Magisk와의 충돌로 인해 모듈을 사용할 수 없습니다!</string>
|
||||
<string name="home_learn_kernelsu">KernelSU 알아보기</string>
|
||||
<string name="home_click_to_learn_kernelsu">KernelSU 설치 방법과 모듈 사용 방법을 확인합니다.</string>
|
||||
<string name="home_support_title">지원이 필요해요!</string>
|
||||
<string name="home_support_content">KernelSU는 지금도, 앞으로도 항상 무료이며 오픈 소스로 유지됩니다. 기부를 통해 여러분의 관심을 보여주세요.</string>
|
||||
<string name="about_source_code"><![CDATA[%1$s에서 소스 코드 보기<br/>%2$s 채널 참가하기]]></string>
|
||||
</resources>
|
||||
68
manager/app/src/main/res/values-nl/strings.xml
Normal file
68
manager/app/src/main/res/values-nl/strings.xml
Normal file
@@ -0,0 +1,68 @@
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">KernelSU</string>
|
||||
|
||||
<string name="home">Home</string>
|
||||
<string name="home_not_installed">Niet geïnstalleerd</string>
|
||||
<string name="home_click_to_install">Klik om te installeren</string>
|
||||
<string name="home_working">Werkend</string>
|
||||
<string name="home_working_version">Versie: %d</string>
|
||||
<string name="home_superuser_count">Superusers: %d</string>
|
||||
<string name="home_module_count">Modules: %d</string>
|
||||
<string name="home_unsupported">Niet ondersteund</string>
|
||||
<string name="home_unsupported_reason">KernelSU ondersteunt alleen GKI kernels</string>
|
||||
<string name="home_copied_to_clipboard">Gekopiëerd naar clipboard</string>
|
||||
<string name="home_support">Ondersteuning</string>
|
||||
|
||||
<string name="home_kernel">Kernel</string>
|
||||
<string name="home_arch">Arch</string>
|
||||
<string name="home_manager_version">Manager Versie</string>
|
||||
<string name="home_api">API Niveau</string>
|
||||
<string name="home_abi">ABI</string>
|
||||
<string name="home_fingerprint">Fingerprint</string>
|
||||
<string name="home_securitypatch">Veiligheidspatch</string>
|
||||
|
||||
<string name="home_selinux_status">SELinux status</string>
|
||||
<string name="selinux_status_disabled">Uitgeschakeld</string>
|
||||
<string name="selinux_status_enforcing">Afgedwongen</string>
|
||||
<string name="selinux_status_permissive">Permissief</string>
|
||||
<string name="selinux_status_unknown">Niet gekend</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="superuser_failed_to_grant_root">Mislukt om root te geven voor %d</string>
|
||||
<string name="superuser_allow_root_confirm">Zeker dat je root wil geven aan %s?</string>
|
||||
<string name="module_failed_to_enable">Mislukt om module in te schakelen: %s</string>
|
||||
<string name="module_failed_to_disable">Mislukt om module uit te schakelen: %s</string>
|
||||
<string name="module_empty">Geen module geïnstalleerd</string>
|
||||
|
||||
<string name="module">Module</string>
|
||||
<string name="uninstall">Verwijderen</string>
|
||||
<string name="module_install">Installeren</string>
|
||||
<string name="install">Installeren</string>
|
||||
<string name="reboot">Herstart</string>
|
||||
<string name="settings">Instellingen</string>
|
||||
<string name="reboot_userspace">Soft herstart</string>
|
||||
<string name="reboot_recovery">Herstart naar Recovery</string>
|
||||
<string name="reboot_bootloader">Herstart naar Bootloader</string>
|
||||
<string name="reboot_download">Herstart om te downloaden</string>
|
||||
<string name="reboot_edl">Herstart naar EDL</string>
|
||||
<string name="about">Over</string>
|
||||
<string name="require_kernel_version_8">Vereist KernelSU versie 8+</string>
|
||||
<string name="module_uninstall_confirm">Zeker van het verwijderen van module %s?</string>
|
||||
<string name="module_uninstall_success">%s verwijderd</string>
|
||||
<string name="module_uninstall_failed">Mislukt om te verwijderen: %s</string>
|
||||
<string name="module_version">Versie</string>
|
||||
<string name="module_author">Auteur</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs is niet beschikbaar, module kan niet werken!</string>
|
||||
<string name="refresh">Vernieuwen</string>
|
||||
<string name="show_system_apps">Toon system apps</string>
|
||||
<string name="hide_system_apps">Verberg system apps</string>
|
||||
<string name="send_log">Stuur Log</string>
|
||||
<string name="safe_mode">Safe mode</string>
|
||||
<string name="reboot_to_apply">Herstart om effect te hebben</string>
|
||||
<string name="module_magisk_conflict">Modules zijn uitgeschakeld omdat ze in conflict zijn met magisk!</string>
|
||||
<string name="home_learn_kernelsu">Leer KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">Leer hoe KernelSU te installeren en modules te gebruiken</string>
|
||||
<string name="home_support_title">Ondersteun ons</string>
|
||||
<string name="home_support_content">KernelSU is, en zal altijd, vrij en open source zijn. Je kan altijd je appreciatie tonen met een donatie.</string>
|
||||
<string name="about_source_code"><![CDATA[Bekijk source code op %1$s<br/>Vervoeg ons %2$s kanaal]]></string>
|
||||
</resources>
|
||||
@@ -25,6 +25,7 @@
|
||||
<string name="selinux_status_unknown">Necunoscut</string>
|
||||
<string name="superuser">Superutilizator</string>
|
||||
<string name="superuser_failed_to_grant_root">Nu s-a putut acorda acces root pentru %d</string>
|
||||
<string name="superuser_allow_root_confirm">Sigur oferi acces root pentru %s?</string>
|
||||
<string name="module_failed_to_enable">Activarea modulului %s a eșuat</string>
|
||||
<string name="module_failed_to_disable">Dezactivarea modulului %s a eșuat</string>
|
||||
<string name="module_empty">Niciun modul instalat</string>
|
||||
@@ -55,4 +56,10 @@
|
||||
<string name="safe_mode">Mod sigur</string>
|
||||
<string name="reboot_to_apply">Repornește pentru ca modificările să intre în vigoare</string>
|
||||
<string name="module_magisk_conflict">Modulele sunt dezactivate deoarece sunt în conflict cu cele ale Magisk-ului!</string>
|
||||
<string name="home_learn_kernelsu">Află mai multe despre KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">Află cum să instalezi KernelSU și să utilizezi module</string>
|
||||
<string name="home_support_title">Suport</string>
|
||||
<string name="home_support_content">KernelSU este, și va fi întotdeauna, gratuit și cu codul sursă deschis. Cu toate acestea, ne poți arăta că îți pasă făcând o donație.</string>
|
||||
<string name="about_source_code"><![CDATA[Vezi codul sursă la %1$s<br/>Alătură-te canalului nostru %2$s]]></string>
|
||||
</resources>
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
<string name="home_click_to_install">Нажмите чтобы установить</string>
|
||||
<string name="home_working">Работает</string>
|
||||
<string name="home_working_version">Версия: %d</string>
|
||||
<string name="home_superuser_count">Superusers: %d</string>
|
||||
<string name="home_module_count">Модули: %d</string>
|
||||
<string name="home_unsupported">Не поддерживается</string>
|
||||
<string name="home_unsupported_reason">KernelSU поддерживает только GKI ядра</string>
|
||||
<string name="home_copied_to_clipboard">Скопировано в буфер обмена</string>
|
||||
@@ -23,7 +25,8 @@
|
||||
<string name="selinux_status_permissive">Разрешающий</string>
|
||||
<string name="selinux_status_unknown">Неизвестно</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="superuser_failed_to_grant_root">Failed to grant root for %d</string>
|
||||
<string name="superuser_failed_to_grant_root">Не удалось предоставить root права для %d</string>
|
||||
<string name="superuser_allow_root_confirm">Вы уверены, что хотите предоставить root права для %s?</string>
|
||||
<string name="module_failed_to_enable">Не удалось включить модуль: %s</string>
|
||||
<string name="module_failed_to_disable">Не удалось отключить модуль: %s</string>
|
||||
<string name="module_empty">Нет установленных модулей</string>
|
||||
@@ -41,6 +44,7 @@
|
||||
<string name="reboot_edl">Reboot to EDL</string>
|
||||
<string name="about">О KernelSU</string>
|
||||
<string name="require_kernel_version_8">Требуется KernelSU версии 8 и выше</string>
|
||||
<string name="module_uninstall_confirm">Вы уверены, что хотите удалить модуль %s?</string>
|
||||
<string name="module_uninstall_success">%s удален</string>
|
||||
<string name="module_uninstall_failed">Не удалось удалить: %s</string>
|
||||
<string name="module_version">Версия</string>
|
||||
@@ -52,5 +56,11 @@
|
||||
<string name="send_log">Отправить лог</string>
|
||||
<string name="safe_mode">Безопасный режим</string>
|
||||
<string name="reboot_to_apply">Перезагрузите, чтобы вступить в силу</string>
|
||||
|
||||
<string name="module_magisk_conflict">Модули отключены, потому что они конфликтуют с модулями Magisk!</string>
|
||||
<string name="home_learn_kernelsu">Узнайте о KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">Узнайте, как установить KernelSU и использовать модули</string>
|
||||
<string name="home_support_title">Поддержите нас</string>
|
||||
<string name="home_support_content">KernelSU был и всегда будет бесплатным и открытым проектом. Однако Вы всегда можете поддержать нас, отправив небольшое пожертвование.</string>
|
||||
<string name="about_source_code"><![CDATA[Посмотреть исходники на %1$s<br/>Присоединяйтесь к каналу %2$s]]></string>
|
||||
</resources>
|
||||
|
||||
66
manager/app/src/main/res/values-tr/strings.xml
Normal file
66
manager/app/src/main/res/values-tr/strings.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<resources>
|
||||
<string name="home">Ana menü</string>
|
||||
<string name="home_not_installed">Yüklü değil</string>
|
||||
<string name="home_click_to_install">Yüklemek için dokun</string>
|
||||
<string name="home_working">Çalışıyor</string>
|
||||
<string name="home_working_version">Sürüm: %d</string>
|
||||
<string name="home_superuser_count">Süper kullanıcılar: %d</string>
|
||||
<string name="home_module_count">Modüller: %d</string>
|
||||
<string name="home_unsupported">Desteklenmiyor</string>
|
||||
<string name="home_unsupported_reason">KernelSU artık yalnızca GKI çekirdeklerini destekliyor</string>
|
||||
<string name="home_copied_to_clipboard">Panoya kopyalandı</string>
|
||||
<string name="home_support">Destek</string>
|
||||
|
||||
<string name="home_kernel">Kernel</string>
|
||||
<string name="home_arch">Arch</string>
|
||||
<string name="home_manager_version">Yönetici Sürümü</string>
|
||||
<string name="home_api">API Seviyesi</string>
|
||||
<string name="home_abi">ABI</string>
|
||||
<string name="home_fingerprint">Parmak izi</string>
|
||||
<string name="home_securitypatch">Güvenlik Yaması</string>
|
||||
|
||||
<string name="home_selinux_status">SELinux durumu</string>
|
||||
<string name="selinux_status_disabled">Devre dışı</string>
|
||||
<string name="selinux_status_enforcing">Zorunlu</string>
|
||||
<string name="selinux_status_permissive">Serbest</string>
|
||||
<string name="selinux_status_unknown">Bilinmiyor</string>
|
||||
<string name="superuser">Süper kullanıcı</string>
|
||||
<string name="superuser_failed_to_grant_root">%d için root erişimi verilemedi</string>
|
||||
<string name="superuser_allow_root_confirm">%s için root erişimi vereceğinizden emin misiniz?</string>
|
||||
<string name="module_failed_to_enable">Modül etkinleştirilemedi: %s</string>
|
||||
<string name="module_failed_to_disable">Modül devre dışı bırakılamadı: %s</string>
|
||||
<string name="module_empty">Yüklü modül yok</string>
|
||||
|
||||
<string name="module">Modül</string>
|
||||
<string name="uninstall">Kaldır</string>
|
||||
<string name="module_install">Yükle</string>
|
||||
<string name="install">Yükle</string>
|
||||
<string name="reboot">Yeniden başlat</string>
|
||||
<string name="settings">Ayarlar</string>
|
||||
<string name="reboot_userspace">Hızlı yeniden Başlat</string>
|
||||
<string name="reboot_recovery">Kurtarma modunda Başlat</string>
|
||||
<string name="reboot_bootloader">Bootloader modunda Başlat</string>
|
||||
<string name="reboot_download">İndirme modunda Başlat</string>
|
||||
<string name="reboot_edl">EDL modunda Başlat</string>
|
||||
<string name="about">Hakkında</string>
|
||||
<string name="require_kernel_version_8">KernelSU sürüm 8+ gerektir</string>
|
||||
<string name="module_uninstall_confirm">%s modülünü kaldırmak istediğinizden emin misiniz?</string>
|
||||
<string name="module_uninstall_success">%s kaldırıldı</string>
|
||||
<string name="module_uninstall_failed">Kaldırılamadı: %s</string>
|
||||
<string name="module_version">Sürüm</string>
|
||||
<string name="module_author">Yazar</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs mevcut değil, modül çalışamıyor!</string>
|
||||
<string name="refresh">Yenile</string>
|
||||
<string name="show_system_apps">Sistem uygulamalarını göster</string>
|
||||
<string name="hide_system_apps">Sistem uygulamalarını gizle</string>
|
||||
<string name="send_log">Log gönder</string>
|
||||
<string name="safe_mode">Güvenli mod</string>
|
||||
<string name="reboot_to_apply">Etkili olması için yeniden başlat</string>
|
||||
<string name="module_magisk_conflict">Modüller Magisk ile çakıştığı için devre dışı bırakıldı!</string>
|
||||
<string name="home_learn_kernelsu">KernelSU\'yu Öğrenin</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">KernelSU\'yu nasıl kuracağınızı ve modülleri nasıl kullanacağınızı öğrenin</string>
|
||||
<string name="home_support_title">Bizi destekle</string>
|
||||
<string name="home_support_content">KernelSU ücretsiz ve açık kaynaktır ve her zaman öyle kalacaktır. Ancak bağış yaparak bize değer verdiğinizi gösterebilirsiniz.</string>
|
||||
<string name="about_source_code"><![CDATA[%1$s kaynak kodunu görüntüle<br/>%2$s kanalımıza katılın]]></string>
|
||||
</resources>
|
||||
66
manager/app/src/main/res/values-uk/strings.xml
Normal file
66
manager/app/src/main/res/values-uk/strings.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<resources>
|
||||
<string name="home">Головна</string>
|
||||
<string name="home_not_installed">Не встановлено</string>
|
||||
<string name="home_click_to_install">Натисніть щоб встановити</string>
|
||||
<string name="home_working">Працює</string>
|
||||
<string name="home_working_version">Версія: %d</string>
|
||||
<string name="home_superuser_count">Суперкористувачі: %d</string>
|
||||
<string name="home_module_count">Модулі: %d</string>
|
||||
<string name="home_unsupported">Не підтримується</string>
|
||||
<string name="home_unsupported_reason">KernelSU підтримує лише ядра GKI</string>
|
||||
<string name="home_copied_to_clipboard">Скопійовано в буфер обміну</string>
|
||||
<string name="home_support">Підтримка</string>
|
||||
|
||||
<string name="home_kernel">Ядро</string>
|
||||
<string name="home_arch">Архітектура</string>
|
||||
<string name="home_manager_version">Версія менеджера</string>
|
||||
<string name="home_api">Рівень API</string>
|
||||
<string name="home_abi">ABI</string>
|
||||
<string name="home_fingerprint">Відбиток</string>
|
||||
<string name="home_securitypatch">Патч безпеки</string>
|
||||
|
||||
<string name="home_selinux_status">Статус SELinux</string>
|
||||
<string name="selinux_status_disabled">Вимкнено</string>
|
||||
<string name="selinux_status_enforcing">Примусовий</string>
|
||||
<string name="selinux_status_permissive">Дозвільний</string>
|
||||
<string name="selinux_status_unknown">Невідомо</string>
|
||||
<string name="superuser">Суперкористувачі</string>
|
||||
<string name="superuser_failed_to_grant_root">Не вдалося надати root-доступ для %d</string>
|
||||
<string name="superuser_allow_root_confirm">Ви впевнені, що хочете надати root-доступ для %s?</string>
|
||||
<string name="module_failed_to_enable">Не вдалося ввімкнути модуль: %s</string>
|
||||
<string name="module_failed_to_disable">Не вдалося вимкнути модуль: %s</string>
|
||||
<string name="module_empty">Немає встановлених модулів</string>
|
||||
|
||||
<string name="module">Модулі</string>
|
||||
<string name="uninstall">Видалити</string>
|
||||
<string name="module_install">Встановити</string>
|
||||
<string name="install">Встановити</string>
|
||||
<string name="reboot">Перезавантажити</string>
|
||||
<string name="settings">Налаштування</string>
|
||||
<string name="reboot_userspace">М\'яке перезавантаження</string>
|
||||
<string name="reboot_recovery">Перезавантажити до Recovery</string>
|
||||
<string name="reboot_bootloader">Перезавантажити до Bootloader</string>
|
||||
<string name="reboot_download">Перезавантажити до Download</string>
|
||||
<string name="reboot_edl">Перезавантажити до EDL</string>
|
||||
<string name="about">Про додаток</string>
|
||||
<string name="require_kernel_version_8">Потрібна версія KernelSU 8+</string>
|
||||
<string name="module_uninstall_confirm">Ви впевнені, що хочете видалити модуль %s?</string>
|
||||
<string name="module_uninstall_success">%s видалено</string>
|
||||
<string name="module_uninstall_failed">Не вдалося видалити: %s</string>
|
||||
<string name="module_version">Версія</string>
|
||||
<string name="module_author">Автор</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs не доступний, модуль не може працювати!</string>
|
||||
<string name="refresh">Оновити</string>
|
||||
<string name="show_system_apps">Показати системні додатки</string>
|
||||
<string name="hide_system_apps">Сховати системні додатки</string>
|
||||
<string name="send_log">Надіслати лог</string>
|
||||
<string name="safe_mode">Безпечний режим</string>
|
||||
<string name="reboot_to_apply">Перезавантажте, щоб застосувати</string>
|
||||
<string name="module_magisk_conflict">Модулі вимкнено, оскільки вони конфліктують із модулями Magisk!</string>
|
||||
<string name="home_learn_kernelsu">Дізнайтеся про KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">Дізнайтеся, як інсталювати KernelSU і використовувати модулі</string>
|
||||
<string name="home_support_title">Підтримайте нас</string>
|
||||
<string name="home_support_content">KernelSU є і завжди буде безкоштовним і з відкритим кодом. Однак Ви можете показати нам, що Вам не байдуже, зробивши пожертву.</string>
|
||||
<string name="about_source_code"><![CDATA[Переглянути вихідний код на %1$s<br/>Приєднуйтесь до нашого каналу %2$s]]></string>
|
||||
</resources>
|
||||
@@ -1,4 +1,13 @@
|
||||
<resources>
|
||||
<string name="home_learn_kernelsu">Tìm hiểu về KernelSU</string>
|
||||
<string name="home_click_to_learn_kernelsu">Cách cài đặt KernelSU và sử dụng mô-đun</string>
|
||||
<string name="home_support_title">Hỗ trợ chúng tôi</string>
|
||||
<string name="home_support_content">KernelSU sẽ luôn luôn miễn phi và mã nguồn mở. Tuy nhiên, bạn có thể cho chúng tôi thấy rằng bạn quan tâm bằng cách gửi một khoản đóng góp nhỏ.</string>
|
||||
<string name="about_source_code"><![CDATA[Xem mã nguồn tại %1$s<br/>Tham gia kênh %2$s của chúng tôi]]></string>
|
||||
<string name="superuser_allow_root_confirm">Bạn có chắc chắn muốn cấp quyền truy cập root cho %s?</string>
|
||||
<string name="module_magisk_conflict">"Các mô-đun bị vô hiệu hóa vì nó xung đột với Magisk!"</string>
|
||||
<string name="module_uninstall_confirm">Bạn có chắc chắn muốn gỡ cài đặt mô-đun %s?</string>
|
||||
<string name="send_log">Gửi nhật ký</string>
|
||||
<string name="home">Trang chủ</string>
|
||||
<string name="home_not_installed">Chưa được cài đặt</string>
|
||||
<string name="home_click_to_install">Nhấn đề cài dặt</string>
|
||||
@@ -48,4 +57,5 @@
|
||||
<string name="hide_system_apps">Ẩn ứng dụng hệ thống</string>
|
||||
<string name="safe_mode">Chế độ an toàn</string>
|
||||
<string name="reboot_to_apply">Khởi động lại để có hiệu lực</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/vi_VN/guide/what-is-kernelsu.html</string>
|
||||
</resources>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<string name="home_click_to_install">点击安装</string>
|
||||
<string name="home_working">工作中</string>
|
||||
<string name="home_working_version">版本: %d</string>
|
||||
<string name="home_superuser_count">超级用户数:%d</string>
|
||||
<string name="home_unsupported">不支持</string>
|
||||
<string name="home_unsupported_reason">KernelSU 现在只支持 GKI 内核</string>
|
||||
<string name="home_copied_to_clipboard">已复制到剪贴板</string>
|
||||
@@ -23,6 +24,7 @@
|
||||
<string name="selinux_status_unknown">未知</string>
|
||||
<string name="superuser">超级用户</string>
|
||||
<string name="superuser_failed_to_grant_root">无法为 %d 授予 Root</string>
|
||||
<string name="superuser_allow_root_confirm">确定要授予 %s ROOT 权限吗?</string>
|
||||
<string name="module_failed_to_enable">无法启用模块: %s</string>
|
||||
<string name="module_failed_to_disable">无法禁用模块: %s</string>
|
||||
<string name="module_empty">没有安装模块</string>
|
||||
@@ -52,4 +54,11 @@
|
||||
<string name="safe_mode">安全模式</string>
|
||||
<string name="reboot_to_apply">重启生效</string>
|
||||
<string name="module_magisk_conflict">所有模块已被禁用,因为它与 Magisk 的模块系统有冲突!</string>
|
||||
<string name="home_module_count">模块数:%d</string>
|
||||
<string name="home_learn_kernelsu">了解 KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/zh_CN/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">了解如何安装 KernelSU 以及如何开发模块</string>
|
||||
<string name="home_support_title">支持开发</string>
|
||||
<string name="home_support_content">KernelSU 将保持免费和开源,向开发者捐赠以表示支持。</string>
|
||||
<string name="about_source_code"><![CDATA[在 %1$s 查看源码<br/>加入我们的 %2$s 频道<br/>加入我们的 <b><a href="https://pd.qq.com/s/8lipl1brp">QQ 频道</a></b>]]></string>
|
||||
</resources>
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
<string name="home_click_to_install">按一下以安裝</string>
|
||||
<string name="home_working">正在處理</string>
|
||||
<string name="home_working_version">版本:%d</string>
|
||||
<string name="home_superuser_count">超級使用者:%d 個</string>
|
||||
<string name="home_module_count">模組:%d 個</string>
|
||||
<string name="home_unsupported">不支援</string>
|
||||
<string name="home_unsupported_reason">KernelSU 現在僅支援 GKI 核心</string>
|
||||
<string name="home_copied_to_clipboard">已複製到剪貼簿</string>
|
||||
@@ -24,7 +26,8 @@
|
||||
<string name="selinux_status_permissive">寬鬆</string>
|
||||
<string name="selinux_status_unknown">未知</string>
|
||||
<string name="superuser">超級使用者</string>
|
||||
<string name="superuser_failed_to_grant_root">無法為 %d 授予 Root 存取權</string>
|
||||
<string name="superuser_failed_to_grant_root">無法為「%d」授予 Root 存取權</string>
|
||||
<string name="superuser_allow_root_confirm">您確定要授予「%s」Root 存取權嗎?</string>
|
||||
<string name="module_failed_to_enable">無法啟用模組:%s</string>
|
||||
<string name="module_failed_to_disable">無法停用模組:%s</string>
|
||||
<string name="module_empty">尚未安裝模組</string>
|
||||
@@ -42,8 +45,8 @@
|
||||
<string name="reboot_edl">重新啟動至 EDL</string>
|
||||
<string name="about">關於</string>
|
||||
<string name="require_kernel_version_8">需要 KernelSU 8+ 版本</string>
|
||||
<string name="module_uninstall_confirm">您確定要解除安裝模組 %s 嗎?</string>
|
||||
<string name="module_uninstall_success">%s 已解除安裝</string>
|
||||
<string name="module_uninstall_confirm">您確定要解除安裝模組「%s」嗎?</string>
|
||||
<string name="module_uninstall_success">「%s」已解除安裝</string>
|
||||
<string name="module_uninstall_failed">無法解除安裝:%s</string>
|
||||
<string name="module_version">版本</string>
|
||||
<string name="module_author">作者</string>
|
||||
@@ -55,4 +58,10 @@
|
||||
<string name="safe_mode">安全模式</string>
|
||||
<string name="reboot_to_apply">重新啟動以生效</string>
|
||||
<string name="module_magisk_conflict">模組已停用,因其與 Magisk 的模組存在衝突!</string>
|
||||
<string name="home_learn_kernelsu">深入瞭解 KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/zh_TW/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">瞭解如何安裝 KernelSU 和使用模組</string>
|
||||
<string name="home_support_title">支持我們</string>
|
||||
<string name="home_support_content">KernelSU 是免費且開源的,您可以透過捐贈來向我們展示您對我們的關心。</string>
|
||||
<string name="about_source_code"><![CDATA[在 %1$s 中檢視原始碼<br/>加入我們的 %2$s 頻道]]></string>
|
||||
</resources>
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
<string name="home_click_to_install">按一下以安裝</string>
|
||||
<string name="home_working">正在處理</string>
|
||||
<string name="home_working_version">版本:%d</string>
|
||||
<string name="home_superuser_count">超級使用者:%d 個</string>
|
||||
<string name="home_module_count">模組:%d 個</string>
|
||||
<string name="home_unsupported">不支援</string>
|
||||
<string name="home_unsupported_reason">KernelSU 現在僅支援 GKI 核心</string>
|
||||
<string name="home_copied_to_clipboard">已複製到剪貼簿</string>
|
||||
@@ -24,7 +26,8 @@
|
||||
<string name="selinux_status_permissive">寬鬆</string>
|
||||
<string name="selinux_status_unknown">未知</string>
|
||||
<string name="superuser">超級使用者</string>
|
||||
<string name="superuser_failed_to_grant_root">無法為 %d 授予 Root 存取權</string>
|
||||
<string name="superuser_failed_to_grant_root">無法為「%d」授予 Root 存取權</string>
|
||||
<string name="superuser_allow_root_confirm">您確定要授予「%s」Root 存取權嗎?</string>
|
||||
<string name="module_failed_to_enable">無法啟用模組:%s</string>
|
||||
<string name="module_failed_to_disable">無法停用模組:%s</string>
|
||||
<string name="module_empty">尚未安裝模組</string>
|
||||
@@ -42,8 +45,8 @@
|
||||
<string name="reboot_edl">重新啟動至 EDL</string>
|
||||
<string name="about">關於</string>
|
||||
<string name="require_kernel_version_8">需要 KernelSU 8+ 版本</string>
|
||||
<string name="module_uninstall_confirm">您確定要解除安裝模組 %s 嗎?</string>
|
||||
<string name="module_uninstall_success">%s 已解除安裝</string>
|
||||
<string name="module_uninstall_confirm">您確定要解除安裝模組「%s」嗎?</string>
|
||||
<string name="module_uninstall_success">「%s」已解除安裝</string>
|
||||
<string name="module_uninstall_failed">無法解除安裝:%s</string>
|
||||
<string name="module_version">版本</string>
|
||||
<string name="module_author">作者</string>
|
||||
@@ -55,4 +58,10 @@
|
||||
<string name="safe_mode">安全模式</string>
|
||||
<string name="reboot_to_apply">重新啟動以生效</string>
|
||||
<string name="module_magisk_conflict">模組已停用,因其與 Magisk 的模組存在衝突!</string>
|
||||
<string name="home_learn_kernelsu">深入瞭解 KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/zh_TW/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">瞭解如何安裝 KernelSU 以及如何開發模組</string>
|
||||
<string name="home_support_title">支持開發</string>
|
||||
<string name="home_support_content">KernelSU 將保持免費和開源,您可以考慮向開發人員捐贈以表示支持。</string>
|
||||
<string name="about_source_code"><![CDATA[在 %1$s 中檢視原始碼<br/>加入我們的 %2$s 頻道]]></string>
|
||||
</resources>
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
<string name="home_click_to_install">Click to install</string>
|
||||
<string name="home_working">Working</string>
|
||||
<string name="home_working_version">Version: %d</string>
|
||||
<string name="home_superuser_count">Superusers: %d</string>
|
||||
<string name="home_module_count">Modules: %d</string>
|
||||
<string name="home_unsupported">Unsupported</string>
|
||||
<string name="home_unsupported_reason">KernelSU only supports GKI kernels now</string>
|
||||
<string name="home_copied_to_clipboard">Copied to clipboard</string>
|
||||
@@ -26,6 +28,7 @@
|
||||
<string name="selinux_status_unknown">Unknown</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="superuser_failed_to_grant_root">Failed to grant root for %d</string>
|
||||
<string name="superuser_allow_root_confirm">Are you sure to grant root access to %s?</string>
|
||||
<string name="module_failed_to_enable">Failed to enable module: %s</string>
|
||||
<string name="module_failed_to_disable">Failed to disable module: %s</string>
|
||||
<string name="module_empty">No module installed</string>
|
||||
@@ -56,5 +59,10 @@
|
||||
<string name="safe_mode">Safe mode</string>
|
||||
<string name="reboot_to_apply">Reboot to take effect</string>
|
||||
<string name="module_magisk_conflict">Modules are disabled because it is conflict with Magisk\'s!</string>
|
||||
|
||||
<string name="home_learn_kernelsu">Learn KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">Learn how to install KernelSU and use modules</string>
|
||||
<string name="home_support_title">Support Us</string>
|
||||
<string name="home_support_content">KernelSU is, and always will be, free, and open source. You can however show us that you care by making a donation.</string>
|
||||
<string name="about_source_code"><![CDATA[View source code at %1$s<br/>Join our %2$s channel]]></string>
|
||||
</resources>
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package me.weishu.kernelsu
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,46 @@
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import com.android.build.gradle.BaseExtension
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.jetbrains.kotlin.konan.properties.Properties
|
||||
import com.android.build.api.dsl.ApplicationDefaultConfig
|
||||
import com.android.build.api.dsl.CommonExtension
|
||||
import com.android.build.gradle.api.AndroidBasePlugin
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
plugins {
|
||||
id("com.android.application") apply false
|
||||
id("com.android.library") apply false
|
||||
kotlin("android") apply false
|
||||
alias(libs.plugins.agp.app) apply false
|
||||
alias(libs.plugins.agp.lib) apply false
|
||||
alias(libs.plugins.kotlin) apply false
|
||||
alias(libs.plugins.lsplugin.cmaker)
|
||||
}
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
cmaker {
|
||||
default {
|
||||
arguments.addAll(
|
||||
arrayOf(
|
||||
"-DANDROID_STL=c++_static",
|
||||
)
|
||||
)
|
||||
val flags = arrayOf(
|
||||
"-Wno-gnu-string-literal-operator-template",
|
||||
"-Wno-c++2b-extensions",
|
||||
)
|
||||
cFlags.addAll(flags)
|
||||
cppFlags.addAll(flags)
|
||||
abiFilters("arm64-v8a", "x86_64")
|
||||
}
|
||||
dependencies {
|
||||
classpath(kotlin("gradle-plugin", version = "1.7.20"))
|
||||
buildTypes {
|
||||
if (it.name == "release") {
|
||||
arguments += "-DDEBUG_SYMBOLS_PATH=${buildDir.absolutePath}/symbols"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val androidMinSdk = 26
|
||||
val androidTargetSdk = 33
|
||||
val androidCompileSdk = 33
|
||||
val androidBuildToolsVersion = "33.0.1"
|
||||
val androidSourceCompatibility = JavaVersion.VERSION_11
|
||||
val androidTargetCompatibility = JavaVersion.VERSION_11
|
||||
val managerVersionCode = getVersionCode()
|
||||
val managerVersionName = getVersionName()
|
||||
val androidMinSdkVersion = 26
|
||||
val androidTargetSdkVersion = 33
|
||||
val androidCompileSdkVersion = 33
|
||||
val androidBuildToolsVersion = "33.0.2"
|
||||
val androidCompileNdkVersion = "25.2.9519653"
|
||||
val androidSourceCompatibility = JavaVersion.VERSION_17
|
||||
val androidTargetCompatibility = JavaVersion.VERSION_17
|
||||
val managerVersionCode by extra(getVersionCode())
|
||||
val managerVersionName by extra(getVersionName())
|
||||
|
||||
tasks.register<Delete>("clean") {
|
||||
delete(rootProject.buildDir)
|
||||
@@ -61,71 +74,31 @@ fun getVersionName(): String {
|
||||
return getGitDescribe()
|
||||
}
|
||||
|
||||
fun Project.configureBaseExtension() {
|
||||
extensions.findByType<BaseExtension>()?.run {
|
||||
compileSdkVersion(androidCompileSdk)
|
||||
buildToolsVersion = androidBuildToolsVersion
|
||||
subprojects {
|
||||
plugins.withType(AndroidBasePlugin::class.java) {
|
||||
extensions.configure(CommonExtension::class.java) {
|
||||
compileSdk = androidCompileSdkVersion
|
||||
ndkVersion = androidCompileNdkVersion
|
||||
buildToolsVersion = androidBuildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdk = androidMinSdk
|
||||
targetSdk = androidTargetSdk
|
||||
versionCode = managerVersionCode
|
||||
versionName = managerVersionName
|
||||
|
||||
consumerProguardFiles("proguard-rules.pro")
|
||||
}
|
||||
|
||||
val signFile = rootProject.file("sign.properties")
|
||||
val config = if (signFile.canRead()) {
|
||||
val prop = Properties()
|
||||
prop.load(signFile.inputStream())
|
||||
signingConfigs.create("config") {
|
||||
storeFile = file(prop.getProperty("KEYSTORE_FILE"))
|
||||
storePassword = prop.getProperty("KEYSTORE_PASSWORD")
|
||||
keyAlias = prop.getProperty("KEY_ALIAS")
|
||||
keyPassword = prop.getProperty("KEY_PASSWORD")
|
||||
}
|
||||
} else {
|
||||
signingConfigs["debug"]
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
all {
|
||||
signingConfig = config
|
||||
}
|
||||
|
||||
named("release") {
|
||||
isMinifyEnabled = true
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = androidSourceCompatibility
|
||||
targetCompatibility = androidTargetCompatibility
|
||||
}
|
||||
|
||||
extensions.findByType<ApplicationExtension>()?.run {
|
||||
buildTypes {
|
||||
named("release") {
|
||||
isShrinkResources = true
|
||||
defaultConfig {
|
||||
minSdk = androidMinSdkVersion
|
||||
if (this is ApplicationDefaultConfig) {
|
||||
targetSdk = androidTargetSdkVersion
|
||||
versionCode = managerVersionCode
|
||||
versionName = managerVersionName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extensions.findByType<KotlinCompile>()?.run {
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
lint {
|
||||
abortOnError = true
|
||||
checkReleaseBuilds = false
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = androidSourceCompatibility
|
||||
targetCompatibility = androidTargetCompatibility
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
plugins.withId("com.android.application") {
|
||||
configureBaseExtension()
|
||||
}
|
||||
plugins.withId("com.android.library") {
|
||||
configureBaseExtension()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,3 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.experimental.enableNewResourceShrinker.preciseShrinking=true
|
||||
android.enableAppCompileTimeRClass=true
|
||||
android.useAndroidX=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
||||
55
manager/gradle/libs.versions.toml
Normal file
55
manager/gradle/libs.versions.toml
Normal file
@@ -0,0 +1,55 @@
|
||||
[versions]
|
||||
agp = "8.0.0"
|
||||
kotlin = "1.8.10"
|
||||
ksp = "1.8.10-1.0.9"
|
||||
compose-bom = "2023.04.01"
|
||||
lifecycle = "2.6.1"
|
||||
accompanist = "0.30.0"
|
||||
navigation = "2.5.3"
|
||||
compose-destination = "1.9.40-beta"
|
||||
libsu = "5.0.5"
|
||||
|
||||
[plugins]
|
||||
agp-app = { id = "com.android.application", version.ref = "agp" }
|
||||
agp-lib = { id = "com.android.library", version.ref = "agp" }
|
||||
kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
||||
lsplugin-apksign = { id = "org.lsposed.lsplugin.apksign", version = "1.1" }
|
||||
lsplugin-cmaker = { id = "org.lsposed.lsplugin.cmaker", version = "1.1" }
|
||||
|
||||
[libraries]
|
||||
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version = "1.7.1" }
|
||||
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" }
|
||||
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
|
||||
androidx-compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" }
|
||||
androidx-compose-material = { group = "androidx.compose.material", name = "material" }
|
||||
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||
androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
||||
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
||||
|
||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
|
||||
androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle" }
|
||||
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version = "lifecycle" }
|
||||
|
||||
com-google-accompanist-drawablepainter = { group = "com.google.accompanist", name = "accompanist-drawablepainter", version.ref = "accompanist" }
|
||||
com-google-accompanist-navigation-animation = { group = "com.google.accompanist", name = "accompanist-navigation-animation", version.ref = "accompanist" }
|
||||
com-google-accompanist-systemuicontroller = { group = "com.google.accompanist", name = "accompanist-systemuicontroller", version.ref = "accompanist" }
|
||||
|
||||
com-github-alorma-compose-settings-ui-m3 = { module = "com.github.alorma:compose-settings-ui-m3", version = "0.22.0" }
|
||||
|
||||
com-github-topjohnwu-libsu-core = { group = "com.github.topjohnwu.libsu", name = "core", version.ref = "libsu" }
|
||||
com-github-topjohnwu-libsu-service = { group = "com.github.topjohnwu.libsu", name = "service", version.ref = "libsu" }
|
||||
|
||||
dev-rikka-rikkax-parcelablelist = { module = "dev.rikka.rikkax.parcelablelist:parcelablelist", version = "2.0.1" }
|
||||
|
||||
io-coil-kt-coil-compose = { group = "io.coil-kt", name = "coil-compose", version = "2.3.0" }
|
||||
|
||||
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version = "1.6.4" }
|
||||
|
||||
me-zhanghai-android-appiconloader-coil = { group = "me.zhanghai.android.appiconloader", name = "appiconloader-coil", version = "1.5.0" }
|
||||
|
||||
compose-destinations-animations-core = { group = "io.github.raamcosta.compose-destinations", name = "animations-core", version.ref = "compose-destination" }
|
||||
compose-destinations-ksp = { group = "io.github.raamcosta.compose-destinations", name = "ksp", version.ref = "compose-destination" }
|
||||
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -6,14 +6,6 @@ pluginManagement {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
plugins {
|
||||
val agp = "7.3.1"
|
||||
val kotlin = "1.7.20"
|
||||
id("com.android.application") version agp
|
||||
id("com.android.library") version agp
|
||||
id("com.google.devtools.ksp") version "$kotlin-1.0.8"
|
||||
kotlin("android") version kotlin
|
||||
}
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
|
||||
39
userspace/ksud/Cargo.lock
generated
39
userspace/ksud/Cargo.lock
generated
@@ -82,9 +82,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.5.3"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
@@ -763,9 +763,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.25"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
|
||||
checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -810,7 +810,6 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"nom",
|
||||
"proc-mounts",
|
||||
"procfs",
|
||||
"regex",
|
||||
"retry",
|
||||
@@ -998,15 +997,6 @@ version = "6.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||
|
||||
[[package]]
|
||||
name = "partition-identity"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa925f9becb532d758b0014b472c576869910929cf4c3f8054b386f19ab9e21"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.4.2"
|
||||
@@ -1093,15 +1083,6 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-mounts"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d652f8435d0ab70bf4f3590a6a851d59604831a458086541b95238cc51ffcf2"
|
||||
dependencies = [
|
||||
"partition-identity",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "procfs"
|
||||
version = "0.15.1"
|
||||
@@ -1374,7 +1355,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "sys-mount"
|
||||
version = "2.0.2"
|
||||
source = "git+https://github.com/tiann/sys-mount#328928573feb9dbe6318d7ebcb8c8c9b681c47cd"
|
||||
source = "git+https://github.com/tiann/sys-mount?branch=loopfix#c7c4048e4a4ffdf8b108a85956363a75f2c554f0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -1426,9 +1407,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.17"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
|
||||
checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"time-core",
|
||||
@@ -1732,7 +1713,7 @@ dependencies = [
|
||||
"hmac",
|
||||
"pbkdf2",
|
||||
"sha1",
|
||||
"time 0.3.17",
|
||||
"time 0.3.20",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
@@ -1766,9 +1747,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.6+zstd.1.5.2"
|
||||
version = "2.0.7+zstd.1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68a3f9792c0c3dc6c165840a75f47ae1f4da402c2d006881129579f6597e801b"
|
||||
checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
|
||||
@@ -35,11 +35,10 @@ which = "4.2.2"
|
||||
getopts = "0.2.21"
|
||||
|
||||
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies]
|
||||
sys-mount = { git = "https://github.com/tiann/sys-mount" }
|
||||
sys-mount = { git = "https://github.com/tiann/sys-mount", branch = "loopfix" }
|
||||
# some android specific dependencies which compiles under unix are also listed here for convenience of coding
|
||||
android-properties = { version = "0.2.2", features = ["bionic-deprecated"] }
|
||||
procfs = "0.15"
|
||||
proc-mounts = "0.3"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android_logger = "0.13"
|
||||
|
||||
@@ -72,6 +72,8 @@ enum Debug {
|
||||
/// Get kernel version
|
||||
Version,
|
||||
|
||||
Mount,
|
||||
|
||||
/// For testing
|
||||
Test,
|
||||
}
|
||||
@@ -187,12 +189,13 @@ pub fn run() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
Debug::Su => crate::ksu::grant_root(),
|
||||
Debug::Mount => event::mount_systemlessly(defs::MODULE_DIR),
|
||||
Debug::Test => todo!(),
|
||||
},
|
||||
};
|
||||
|
||||
if let Err(e) = &result {
|
||||
log::error!("Error: {}", e);
|
||||
log::error!("Error: {:?}", e);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ use const_format::concatcp;
|
||||
pub const ADB_DIR: &str = "/data/adb/";
|
||||
pub const WORKING_DIR: &str = concatcp!(ADB_DIR, "ksu/");
|
||||
pub const BINARY_DIR: &str = concatcp!(WORKING_DIR, "bin/");
|
||||
pub const LOG_DIR: &str = concatcp!(WORKING_DIR, "log/");
|
||||
|
||||
pub const KSURC_PATH: &str = concatcp!(WORKING_DIR, ".ksurc");
|
||||
pub const KSU_OVERLAY_SOURCE: &str = "KSU";
|
||||
pub const DAEMON_PATH: &str = concatcp!(ADB_DIR, "ksud");
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
|
||||
@@ -1,50 +1,28 @@
|
||||
use anyhow::{bail, Context, Result};
|
||||
use log::{info, warn};
|
||||
use std::path::PathBuf;
|
||||
use std::{collections::HashMap, path::Path};
|
||||
|
||||
use crate::{
|
||||
assets, defs, mount,
|
||||
assets, defs, mount, restorecon,
|
||||
utils::{self, ensure_clean_dir, ensure_dir_exists},
|
||||
};
|
||||
|
||||
fn mount_partition(partition: &str, lowerdir: &mut Vec<String>) -> Result<()> {
|
||||
fn mount_partition(partition: &str, lowerdir: &Vec<String>) -> Result<()> {
|
||||
if lowerdir.is_empty() {
|
||||
warn!("partition: {partition} lowerdir is empty");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let partition = format!("/{partition}");
|
||||
|
||||
// if /partition is a symlink and linked to /system/partition, then we don't need to overlay it separately
|
||||
if Path::new(&format!("/{partition}")).read_link().is_ok() {
|
||||
if Path::new(&partition).read_link().is_ok() {
|
||||
warn!("partition: {partition} is a symlink");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// handle stock mounts under /partition, we should restore the mount point after overlay
|
||||
// because the overlayfs mount will "overlay" the bind mount such as /vendor/bt_firmware, /vendor/dsp
|
||||
// which will cause the system bootloop or bluetooth/dsp not working
|
||||
let stock_mount = mount::StockMount::new(&format!("/{partition}/"))
|
||||
.with_context(|| format!("get stock mount of partition: {partition} failed"))?;
|
||||
|
||||
// add /partition as the lowerest dir
|
||||
let lowest_dir = format!("/{partition}");
|
||||
lowerdir.push(lowest_dir.clone());
|
||||
|
||||
let lowerdir = lowerdir.join(":");
|
||||
info!("partition: {partition} lowerdir: {lowerdir}");
|
||||
|
||||
let result = mount::mount_overlay(&lowerdir, &lowest_dir);
|
||||
|
||||
if let Err(e) = stock_mount.remount() {
|
||||
if result.is_ok() {
|
||||
// if mount overlay ok but stock remount failed, we should umount overlay
|
||||
warn!("remount stock failed: {:?}, umount overlay {lowest_dir}", e);
|
||||
if mount::umount_dir(&lowest_dir).is_err() {
|
||||
warn!("umount overlay {lowest_dir} failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
mount::mount_overlay(&partition, lowerdir)
|
||||
}
|
||||
|
||||
pub fn mount_systemlessly(module_dir: &str) -> Result<()> {
|
||||
@@ -74,7 +52,7 @@ pub fn mount_systemlessly(module_dir: &str) -> Result<()> {
|
||||
}
|
||||
|
||||
let module_system = Path::new(&module).join("system");
|
||||
if module_system.exists() {
|
||||
if module_system.is_dir() {
|
||||
system_lowerdir.push(format!("{}", module_system.display()));
|
||||
}
|
||||
|
||||
@@ -82,24 +60,23 @@ pub fn mount_systemlessly(module_dir: &str) -> Result<()> {
|
||||
// if /partition is a mountpoint, we would move it to $MODPATH/$partition when install
|
||||
// otherwise it must be a symlink and we don't need to overlay!
|
||||
let part_path = Path::new(&module).join(part);
|
||||
if !part_path.exists() {
|
||||
continue;
|
||||
}
|
||||
if let Some(v) = partition_lowerdir.get_mut(*part) {
|
||||
v.push(format!("{}", part_path.display()));
|
||||
if part_path.is_dir() {
|
||||
if let Some(v) = partition_lowerdir.get_mut(*part) {
|
||||
v.push(format!("{}", part_path.display()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mount /system first
|
||||
if let Err(e) = mount_partition("system", &mut system_lowerdir) {
|
||||
warn!("mount system failed: {e}");
|
||||
if let Err(e) = mount_partition("system", &system_lowerdir) {
|
||||
warn!("mount system failed: {:#}", e);
|
||||
}
|
||||
|
||||
// mount other partitions
|
||||
for (k, mut v) in partition_lowerdir {
|
||||
if let Err(e) = mount_partition(&k, &mut v) {
|
||||
warn!("mount {k} failed: {e}");
|
||||
for (k, v) in partition_lowerdir {
|
||||
if let Err(e) = mount_partition(&k, &v) {
|
||||
warn!("mount {k} failed: {:#}", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +86,9 @@ pub fn mount_systemlessly(module_dir: &str) -> Result<()> {
|
||||
pub fn on_post_data_fs() -> Result<()> {
|
||||
crate::ksu::report_post_fs_data();
|
||||
|
||||
#[cfg(unix)]
|
||||
let _ = catch_bootlog();
|
||||
|
||||
if utils::has_magisk() {
|
||||
warn!("Magisk detected, skip post-fs-data!");
|
||||
return Ok(());
|
||||
@@ -184,17 +164,12 @@ pub fn on_post_data_fs() -> Result<()> {
|
||||
warn!("load system.prop failed: {}", e);
|
||||
}
|
||||
|
||||
// Finally, we should do systemless mount
|
||||
// But we should umount all stock overlayfs and remount them after module mounted
|
||||
let stock_overlay = mount::StockOverlay::new();
|
||||
stock_overlay.umount_all();
|
||||
|
||||
// mount moduke systemlessly by overlay
|
||||
// mount module systemlessly by overlay
|
||||
if let Err(e) = mount_systemlessly(module_dir) {
|
||||
warn!("do systemless mount failed: {}", e);
|
||||
}
|
||||
|
||||
stock_overlay.mount_all();
|
||||
std::env::set_current_dir("/").with_context(|| "failed to chdir to /")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -246,7 +221,7 @@ pub fn daemon() -> Result<()> {
|
||||
pub fn install() -> Result<()> {
|
||||
ensure_dir_exists(defs::ADB_DIR)?;
|
||||
std::fs::copy("/proc/self/exe", defs::DAEMON_PATH)?;
|
||||
|
||||
restorecon::setcon(defs::DAEMON_PATH, restorecon::ADB_CON)?;
|
||||
// install binary assets
|
||||
assets::ensure_binaries().with_context(|| "Failed to extract assets")?;
|
||||
|
||||
@@ -258,7 +233,6 @@ pub fn install() -> Result<()> {
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
fn link_ksud_to_bin() -> Result<()> {
|
||||
use std::path::PathBuf;
|
||||
let ksu_bin = PathBuf::from(defs::DAEMON_PATH);
|
||||
let ksu_bin_link = PathBuf::from(defs::DAEMON_LINK_PATH);
|
||||
if ksu_bin.exists() && !ksu_bin_link.exists() {
|
||||
@@ -266,3 +240,42 @@ fn link_ksud_to_bin() -> Result<()> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn catch_bootlog() -> Result<()> {
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::process::Stdio;
|
||||
|
||||
let logdir = Path::new(defs::LOG_DIR);
|
||||
utils::ensure_dir_exists(logdir)?;
|
||||
let bootlog = logdir.join("boot.log");
|
||||
let oldbootlog = logdir.join("boot.old.log");
|
||||
|
||||
if bootlog.exists() {
|
||||
std::fs::rename(&bootlog, oldbootlog)?;
|
||||
}
|
||||
|
||||
let bootlog = std::fs::File::create(bootlog)?;
|
||||
|
||||
// timeout -s 9 30s logcat > boot.log
|
||||
let result = unsafe {
|
||||
std::process::Command::new("timeout")
|
||||
.process_group(0)
|
||||
.pre_exec(|| {
|
||||
utils::switch_cgroups();
|
||||
Ok(())
|
||||
})
|
||||
.arg("-s")
|
||||
.arg("9")
|
||||
.arg("30s")
|
||||
.arg("logcat")
|
||||
.stdout(Stdio::from(bootlog))
|
||||
.spawn()
|
||||
};
|
||||
|
||||
if let Err(e) = result {
|
||||
warn!("Failed to start logcat: {:#}", e);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -278,7 +278,9 @@ mark_remove() {
|
||||
}
|
||||
|
||||
mark_replace() {
|
||||
mkdir -p ${1%/*} 2>/dev/null
|
||||
# REPLACE must be directory!!!
|
||||
# https://docs.kernel.org/filesystems/overlayfs.html#whiteouts-and-opaque-directories
|
||||
mkdir -p $1 2>/dev/null
|
||||
setfattr -n trusted.overlay.opaque -v y $1
|
||||
chmod 644 $1
|
||||
}
|
||||
@@ -389,10 +391,6 @@ install_module() {
|
||||
[ -f $MODPATH/customize.sh ] && . $MODPATH/customize.sh
|
||||
fi
|
||||
|
||||
handle_partition vendor
|
||||
handle_partition system_ext
|
||||
handle_partition product
|
||||
|
||||
# Handle replace folders
|
||||
for TARGET in $REPLACE; do
|
||||
ui_print "- Replace target: $TARGET"
|
||||
@@ -405,6 +403,10 @@ install_module() {
|
||||
mark_remove $MODPATH$TARGET
|
||||
done
|
||||
|
||||
handle_partition vendor
|
||||
handle_partition system_ext
|
||||
handle_partition product
|
||||
|
||||
if $BOOTMODE; then
|
||||
mktouch $NVBASE/modules/$MODID/update
|
||||
rm -rf $NVBASE/modules/$MODID/remove 2>/dev/null
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::{Ok, Result};
|
||||
use anyhow::{bail, Ok, Result};
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use anyhow::Context;
|
||||
@@ -7,10 +7,11 @@ use retry::delay::NoDelay;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use sys_mount::{unmount, FilesystemType, Mount, MountFlags, Unmount, UnmountFlags};
|
||||
|
||||
use crate::defs::KSU_OVERLAY_SOURCE;
|
||||
use log::{info, warn};
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use procfs::process::{MountInfo, Process};
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use std::collections::HashSet;
|
||||
use procfs::process::Process;
|
||||
use std::path::Path;
|
||||
|
||||
pub struct AutoMountExt4 {
|
||||
mnt: String,
|
||||
@@ -25,6 +26,7 @@ impl AutoMountExt4 {
|
||||
let result = Mount::builder()
|
||||
.fstype(FilesystemType::from("ext4"))
|
||||
.flags(MountFlags::empty())
|
||||
.create_loop(true)
|
||||
.mount(src, mnt)
|
||||
.map(|mount| {
|
||||
Ok(Self {
|
||||
@@ -99,6 +101,7 @@ fn mount_image(src: &str, target: &str, autodrop: bool) -> Result<()> {
|
||||
if autodrop {
|
||||
Mount::builder()
|
||||
.fstype(FilesystemType::from("ext4"))
|
||||
.create_loop(true)
|
||||
.mount_autodrop(src, target, UnmountFlags::empty())
|
||||
.with_context(|| format!("Failed to do mount: {src} -> {target}"))?;
|
||||
} else {
|
||||
@@ -128,14 +131,134 @@ pub fn umount_dir(src: &str) -> Result<()> {
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn mount_overlay(lowerdir: &str, mnt: &str) -> Result<()> {
|
||||
fn mount_overlayfs(
|
||||
lower_dirs: &[String],
|
||||
lowest: impl AsRef<Path>,
|
||||
dest: impl AsRef<Path>,
|
||||
) -> Result<()> {
|
||||
let options = format!(
|
||||
"lowerdir={}:{}",
|
||||
lower_dirs.join(":"),
|
||||
lowest.as_ref().display()
|
||||
);
|
||||
info!(
|
||||
"mount overlayfs on {}, options={}",
|
||||
dest.as_ref().display(),
|
||||
options
|
||||
);
|
||||
Mount::builder()
|
||||
.fstype(FilesystemType::from("overlay"))
|
||||
.data(&options)
|
||||
.flags(MountFlags::RDONLY)
|
||||
.data(&format!("lowerdir={lowerdir}"))
|
||||
.mount("overlay", mnt)
|
||||
.map(|_| ())
|
||||
.map_err(|e| anyhow::anyhow!("mount partition: {mnt} overlay failed: {e}"))
|
||||
.mount(KSU_OVERLAY_SOURCE, dest.as_ref())
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"mount overlayfs on {} options {} failed",
|
||||
dest.as_ref().display(),
|
||||
options
|
||||
)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn bind_mount(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<()> {
|
||||
info!(
|
||||
"bind mount {} -> {}",
|
||||
from.as_ref().display(),
|
||||
to.as_ref().display()
|
||||
);
|
||||
Mount::builder()
|
||||
.flags(MountFlags::BIND)
|
||||
.mount(from.as_ref(), to.as_ref())
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"bind mount failed: {} -> {}",
|
||||
from.as_ref().display(),
|
||||
to.as_ref().display()
|
||||
)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn mount_overlay_child(
|
||||
mount_point: &str,
|
||||
relative: &String,
|
||||
module_roots: &Vec<String>,
|
||||
stock_root: &String,
|
||||
) -> Result<()> {
|
||||
if !module_roots
|
||||
.iter()
|
||||
.any(|lower| Path::new(&format!("{lower}{relative}")).exists())
|
||||
{
|
||||
return bind_mount(stock_root, mount_point);
|
||||
}
|
||||
if !Path::new(&stock_root).is_dir() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut lower_dirs: Vec<String> = vec![];
|
||||
for lower in module_roots {
|
||||
let lower_dir = format!("{lower}{relative}");
|
||||
let path = Path::new(&lower_dir);
|
||||
if path.is_dir() {
|
||||
lower_dirs.push(lower_dir);
|
||||
} else if path.exists() {
|
||||
// stock root has been blocked by this file
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
if lower_dirs.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
// merge modules and stock
|
||||
if let Err(e) = mount_overlayfs(&lower_dirs, stock_root, mount_point) {
|
||||
warn!("failed: {:#}, fallback to bind mount", e);
|
||||
bind_mount(stock_root, mount_point)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn mount_overlay(root: &String, module_roots: &Vec<String>) -> Result<()> {
|
||||
info!("mount overlay for {}", root);
|
||||
std::env::set_current_dir(root).with_context(|| format!("failed to chdir to {root}"))?;
|
||||
let stock_root = ".";
|
||||
|
||||
// collect child mounts before mounting the root
|
||||
let mounts = Process::myself()?
|
||||
.mountinfo()
|
||||
.with_context(|| "get mountinfo")?;
|
||||
let mut mount_seq = mounts
|
||||
.iter()
|
||||
.filter(|m| {
|
||||
m.mount_point.starts_with(root) && !Path::new(&root).starts_with(&m.mount_point)
|
||||
})
|
||||
.map(|m| m.mount_point.to_str())
|
||||
.collect::<Vec<_>>();
|
||||
mount_seq.sort();
|
||||
mount_seq.dedup();
|
||||
|
||||
mount_overlayfs(module_roots, root, root).with_context(|| "mount overlayfs for root failed")?;
|
||||
for mount_point in mount_seq.iter() {
|
||||
let Some(mount_point) = mount_point else {
|
||||
continue;
|
||||
};
|
||||
let relative = mount_point.replacen(root, "", 1);
|
||||
let stock_root: String = format!("{stock_root}{relative}");
|
||||
if !Path::new(&stock_root).exists() {
|
||||
continue;
|
||||
}
|
||||
if let Err(e) = mount_overlay_child(mount_point, &relative, module_roots, &stock_root) {
|
||||
warn!(
|
||||
"failed to mount overlay for child {}: {:#}, revert",
|
||||
mount_point, e
|
||||
);
|
||||
umount_dir(root).with_context(|| format!("failed to revert {root}"))?;
|
||||
bail!(e);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
@@ -149,185 +272,6 @@ pub fn umount_dir(_src: &str) -> Result<()> {
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
pub fn mount_overlay(_lowerdir: &str, _mnt: &str) -> Result<()> {
|
||||
pub fn mount_overlay(_dest: &String, _lower_dirs: &Vec<String>) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub struct StockOverlay {
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
mountinfos: Vec<MountInfo>,
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
impl StockOverlay {
|
||||
pub fn new() -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn mount_all(&self) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn umount_all(&self) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
impl StockOverlay {
|
||||
pub fn new() -> Self {
|
||||
if let std::result::Result::Ok(process) = Process::myself() {
|
||||
if let std::result::Result::Ok(mountinfos) = process.mountinfo() {
|
||||
let overlay_mounts = mountinfos
|
||||
.into_iter()
|
||||
.filter(|m| m.fs_type == "overlay")
|
||||
.collect::<Vec<_>>();
|
||||
return Self {
|
||||
mountinfos: overlay_mounts,
|
||||
};
|
||||
}
|
||||
}
|
||||
Self { mountinfos: vec![] }
|
||||
}
|
||||
|
||||
pub fn mount_all(&self) {
|
||||
log::info!("stock overlay: mount all: {:?}", self.mountinfos);
|
||||
for mount in self.mountinfos.clone() {
|
||||
let Some(mnt) = mount.mount_point.to_str() else {
|
||||
log::warn!("Failed to get mount point");
|
||||
continue;
|
||||
};
|
||||
|
||||
if mnt == "/system" {
|
||||
log::warn!("stock overlay found /system, skip!");
|
||||
continue;
|
||||
}
|
||||
|
||||
let (_flags, b): (HashSet<_>, HashSet<_>) = mount
|
||||
.mount_options
|
||||
.into_iter()
|
||||
.chain(mount.super_options)
|
||||
.partition(|(_, m)| m.is_none());
|
||||
|
||||
let mut overlay_opts = vec![];
|
||||
for (opt, val) in b {
|
||||
if let Some(val) = val {
|
||||
overlay_opts.push(format!("{opt}={val}"));
|
||||
} else {
|
||||
log::warn!("opt empty: {}", opt);
|
||||
}
|
||||
}
|
||||
let overlay_data = overlay_opts.join(",");
|
||||
let result = Mount::builder()
|
||||
.fstype(FilesystemType::from("overlay"))
|
||||
.flags(MountFlags::RDONLY)
|
||||
.data(&overlay_data)
|
||||
.mount("overlay", mnt);
|
||||
if let Err(e) = result {
|
||||
log::error!(
|
||||
"stock mount overlay: {} failed: {}",
|
||||
mount.mount_point.display(),
|
||||
e
|
||||
);
|
||||
} else {
|
||||
log::info!(
|
||||
"stock mount :{} overlay_opts: {}",
|
||||
mount.mount_point.display(),
|
||||
overlay_opts.join(",")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn umount_all(&self) {
|
||||
log::info!("stock overlay: umount all: {:?}", self.mountinfos);
|
||||
for mnt in &self.mountinfos {
|
||||
let Some(p) = mnt.mount_point.to_str() else {
|
||||
log::warn!("Failed to umount: {}", mnt.mount_point.display());
|
||||
continue;
|
||||
};
|
||||
|
||||
let result = umount_dir(p);
|
||||
log::info!("stock umount {}: {:?}", p, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// some ROMs mount device(ext4,exfat) to /vendor, when we do overlay mount, it will overlay
|
||||
// the stock mounts, these mounts include bt_firmware, wifi_firmware, etc.
|
||||
// so we to remount these mounts when we do overlay mount.
|
||||
// this is a workaround, we should find a better way to do this.
|
||||
#[derive(Debug)]
|
||||
pub struct StockMount {
|
||||
mnt: String,
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
mountlist: proc_mounts::MountList,
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
impl StockMount {
|
||||
pub fn new(mnt: &str) -> Result<Self> {
|
||||
let mountlist = proc_mounts::MountList::new()?;
|
||||
Ok(Self {
|
||||
mnt: mnt.to_string(),
|
||||
mountlist,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_target_mounts(&self) -> Vec<&proc_mounts::MountInfo> {
|
||||
let mut mounts = self
|
||||
.mountlist
|
||||
.destination_starts_with(std::path::Path::new(&self.mnt))
|
||||
.filter(|m| m.fstype != "overlay" && m.fstype != "rootfs")
|
||||
.collect::<Vec<_>>();
|
||||
mounts.sort_by(|a, b| b.dest.cmp(&a.dest)); // inverse order
|
||||
mounts
|
||||
}
|
||||
|
||||
pub fn remount(&self) -> Result<()> {
|
||||
let mut mounts = self.get_target_mounts();
|
||||
mounts.reverse(); // remount it in order
|
||||
log::info!("remount stock for {} : {:?}", self.mnt, mounts);
|
||||
for m in mounts {
|
||||
let src = std::fs::canonicalize(&m.source)?;
|
||||
|
||||
let src = src.to_str().ok_or(anyhow::anyhow!("Failed to get src"))?;
|
||||
let dst = m
|
||||
.dest
|
||||
.to_str()
|
||||
.ok_or(anyhow::anyhow!("Failed to get dst"))?;
|
||||
|
||||
let fstype = m.fstype.as_str();
|
||||
let options = m.options.join(",");
|
||||
|
||||
log::info!("begin remount: {src} -> {dst}");
|
||||
let result = std::process::Command::new("mount")
|
||||
.arg("-t")
|
||||
.arg(fstype)
|
||||
.arg("-o")
|
||||
.arg(options)
|
||||
.arg(src)
|
||||
.arg(dst)
|
||||
.status();
|
||||
if let Err(e) = result {
|
||||
log::error!("remount failed: {}", e);
|
||||
} else {
|
||||
log::info!("remount {src} -> {dst} succeed!");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
impl StockMount {
|
||||
pub fn new(mnt: &str) -> Result<Self> {
|
||||
Ok(Self {
|
||||
mnt: mnt.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn remount(&self) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ use anyhow::{Context, Ok};
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use extattr::{setxattr, Flags as XattrFlags};
|
||||
|
||||
const SYSTEM_CON: &str = "u:object_r:system_file:s0";
|
||||
const _ADB_CON: &str = "u:object_r:adb_data_file:s0";
|
||||
pub const SYSTEM_CON: &str = "u:object_r:system_file:s0";
|
||||
pub const ADB_CON: &str = "u:object_r:adb_data_file:s0";
|
||||
const SELINUX_XATTR: &str = "security.selinux";
|
||||
|
||||
pub fn setcon<P: AsRef<Path>>(path: P, con: &str) -> Result<()> {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
import en from './en'
|
||||
import zh_CN from './zh_CN'
|
||||
import zh_TW from './zh_TW'
|
||||
import vi_VN from './vi_VN'
|
||||
import id_ID from './id_ID'
|
||||
|
||||
@@ -18,6 +19,12 @@ export default defineConfig({
|
||||
themeConfig: zh_CN.themeConfig,
|
||||
description: zh_CN.description
|
||||
},
|
||||
zh_TW: {
|
||||
label: '繁體中文',
|
||||
lang: zh_TW.lang,
|
||||
themeConfig: zh_TW.themeConfig,
|
||||
description: zh_TW.description
|
||||
},
|
||||
vi_VN: {
|
||||
label: 'Tiếng Việt',
|
||||
lang: vi_VN.lang,
|
||||
|
||||
60
website/docs/.vitepress/locales/zh_TW.ts
Normal file
60
website/docs/.vitepress/locales/zh_TW.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { createRequire } from 'module'
|
||||
import { defineConfig } from 'vitepress'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
const pkg = require('vitepress/package.json')
|
||||
|
||||
export default defineConfig({
|
||||
lang: 'zh-TW',
|
||||
description: '一個以核心為基礎,適用於 Android GKI 的 Root 解決方案。',
|
||||
|
||||
themeConfig: {
|
||||
nav: nav(),
|
||||
|
||||
lastUpdatedText: '上次更新',
|
||||
|
||||
sidebar: {
|
||||
'/zh_TW/guide/': sidebarGuide()
|
||||
},
|
||||
|
||||
socialLinks: [
|
||||
{ icon: 'github', link: 'https://github.com/tiann/KernelSU' }
|
||||
],
|
||||
|
||||
footer: {
|
||||
message: '係依據 GPL3 授權發行。',
|
||||
copyright: 'Copyright © 2022-目前 KernelSU 開發人員'
|
||||
},
|
||||
|
||||
editLink: {
|
||||
pattern: 'https://github.com/tiann/KernelSU/edit/main/website/docs/:path',
|
||||
text: '在 GitHub 中編輯本頁面'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function nav() {
|
||||
return [
|
||||
{ text: '指南', link: '/zh_TW/guide/what-is-kernelsu' },
|
||||
{ text: 'Github', link: 'https://github.com/tiann/KernelSU' }
|
||||
]
|
||||
}
|
||||
|
||||
function sidebarGuide() {
|
||||
return [
|
||||
{
|
||||
text: 'Guide',
|
||||
items: [
|
||||
{ text: '什麼是 KernelSU?', link: '/zh_TW/guide/what-is-kernelsu' },
|
||||
{ text: '安裝', link: '/zh_TW/guide/installation' },
|
||||
{ text: '如何建置?', link: '/zh_TW/guide/how-to-build' },
|
||||
{ text: '如何為非 GKI 核心整合 KernelSU', link: '/zh_TW/guide/how-to-integrate-for-non-gki'},
|
||||
{ text: '非官方支援裝置', link: '/zh_TW/guide/unofficially-support-devices.md' },
|
||||
{ text: '模組指南', link: '/zh_TW/guide/module.md' },
|
||||
{ text: '搶救開機迴圈', link: '/zh_TW/guide/rescue-from-bootloop.md' },
|
||||
{ text: '常見問題', link: '/zh_TW/guide/faq' },
|
||||
{ text: '隱藏功能', link: '/zh_TW/guide/hidden-features' },
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -16,7 +16,7 @@ Yes, But it is in early version, it may be buggy. Please wait for it to be stabl
|
||||
|
||||
## Does KernelSU support Xposed?
|
||||
|
||||
Yes, [Dreamland](https://github.com/canyie/Dreamland) and [TaiChi](https::/taichi.cool) work now. For LSPosed, you can make it work by [Zygisk on KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU)
|
||||
Yes, [Dreamland](https://github.com/canyie/Dreamland) and [TaiChi](https://taichi.cool) work now. For LSPosed, you can make it work by [Zygisk on KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU)
|
||||
|
||||
## Does KernelSU support Zygisk?
|
||||
|
||||
|
||||
@@ -42,10 +42,24 @@ tools/bazel build --config=fast //common:kernel_aarch64_dist
|
||||
|
||||
## Build Kernel with KernelSU
|
||||
|
||||
If you can build the kernel successfully, then build KernelSU is so easy, run this command in Kernel source root dir:
|
||||
If you can build the kernel successfully, then build KernelSU is so easy, Select any one run in Kernel source root dir:
|
||||
|
||||
- Latest tag(stable)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
|
||||
```
|
||||
|
||||
- main branch(dev)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
- Select tag(Such as v0.5.2)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
|
||||
```
|
||||
|
||||
And then rebuild the kernel and you will get a kernel image with KernelSU!
|
||||
|
||||
@@ -46,10 +46,24 @@ If kprobe can not work in your kernel (maybe a upstream bug or kernel below 4.8)
|
||||
|
||||
First, add KernelSU to your kernel source tree:
|
||||
|
||||
- Latest tag(stable)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
|
||||
```
|
||||
|
||||
- main branch(dev)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
- Select tag(Such as v0.5.2)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
|
||||
```
|
||||
|
||||
Then, add KernelSU calls to the kernel source, here is a patch to refer:
|
||||
|
||||
```diff
|
||||
|
||||
@@ -16,7 +16,7 @@ Ya, Tetapi masih dalam versi awal, bisa jadi ngebug. Mohon tunggu sampai semuany
|
||||
|
||||
## Apakah KernelSU mendukung Xposed?
|
||||
|
||||
Ya, [Dreamland](https://github.com/canyie/Dreamland) dan [TaiChi](https::/taichi.cool) sekarang bekerja sebagian, Dan kita sedang mencoba untuk membuat Xposed Framework lainnya bekerja.
|
||||
Ya, [Dreamland](https://github.com/canyie/Dreamland) dan [TaiChi](https://taichi.cool) sekarang bekerja sebagian, Dan kita sedang mencoba untuk membuat Xposed Framework lainnya bekerja.
|
||||
|
||||
## Apakah KernelSU kompatibel dengan Magisk?
|
||||
|
||||
|
||||
@@ -42,8 +42,22 @@ tools/bazel build --config=fast //common:kernel_aarch64_dist
|
||||
|
||||
Jika Anda dapat build kernel dengan sukses, maka build KernelSU sangatlah mudah, jalankan perintah ini di root dir kernel source:
|
||||
|
||||
- Latest tag(stable)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
|
||||
```
|
||||
|
||||
- main branch(dev)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
- Select tag(Such as v0.5.2)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
|
||||
```
|
||||
|
||||
Dan kemudian build ulang kernel dan Anda akan mendapatkan image kernel dengan KernelSU!
|
||||
|
||||
@@ -17,10 +17,24 @@ KernelSU menggunakan kprobe untuk melakukan hook kernel, jika *kprobe* berjalan
|
||||
|
||||
Pertama, tambahkan KernelSU ke dalam berkas kernel source tree:
|
||||
|
||||
- Latest tag(stable)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
|
||||
```
|
||||
|
||||
- main branch(dev)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
- Select tag(Such as v0.5.2)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
|
||||
```
|
||||
|
||||
Kemudian, Anda harus memeriksa apakah *kprobe* diaktifkan dalam konfigurasi kernel Anda, jika tidak, tambahkan konfigurasi ini ke dalamnya:
|
||||
|
||||
```
|
||||
|
||||
@@ -1,4 +1,18 @@
|
||||
[
|
||||
{
|
||||
"maintainer": "diphons",
|
||||
"maintainer_link": "https://github.com/diphons",
|
||||
"kernel_name": "D8G_Kernel_oxygen",
|
||||
"kernel_link": "https://github.com/diphons/D8G_Kernel_oxygen/tree/13",
|
||||
"devices": "Poco F3: alioth | Poco F4: munch | MI10T/PRO: Apollo"
|
||||
},
|
||||
{
|
||||
"maintainer": "diphons",
|
||||
"maintainer_link": "https://github.com/diphons",
|
||||
"kernel_name": "D8G_Kernel_ROG",
|
||||
"kernel_link": "https://github.com/diphons/D8G_Kernel_Vayuasu/tree/main",
|
||||
"devices": "Poco X3 Pro: Vayu | Bhima"
|
||||
},
|
||||
{
|
||||
"maintainer": "SpectreDev-007",
|
||||
"maintainer_link": "https://github.com/SpectreDev-007",
|
||||
@@ -97,6 +111,13 @@
|
||||
"kernel_link": "https://github.com/lateautumn233/android_kernel_oneplus_sm8250",
|
||||
"devices": "OnePlus 8 Serials"
|
||||
},
|
||||
{
|
||||
"maintainer": "Sreeshankar K",
|
||||
"maintainer_link": "https://github.com/Sanju0910",
|
||||
"kernel_name": "android_kernel_oneplus_avicii",
|
||||
"kernel_link": "https://github.com/Sanju0910/android_kernel_oneplus_avicii",
|
||||
"devices": "OnePlus Nord (avicii)"
|
||||
},
|
||||
{
|
||||
"maintainer": "Molyuu",
|
||||
"maintainer_link": "https://github.com/Molyuu",
|
||||
@@ -189,10 +210,94 @@
|
||||
"devices": "Asus Zenfone Max Pro M1/M2"
|
||||
},
|
||||
{
|
||||
"maintainer": "TheNoFace",
|
||||
"maintainer_link": "https://github.com/TheNoFace",
|
||||
"kernel_name": "kernel_oneplus_msm8998",
|
||||
"kernel_link": "https://github.com/TheNoFace/kernel_oneplus_msm8998",
|
||||
"devices": "OnePlus 5/5T"
|
||||
"maintainer": "Evans Mike",
|
||||
"maintainer_link": "https://github.com/etnperlong",
|
||||
"kernel_name": "kernel_xiaomi_raphael_bool-x",
|
||||
"kernel_link": "https://github.com/etnperlong/kernel_xiaomi_raphael_bool-x",
|
||||
"devices": "Xiaomi Redmi K20 Pro / Mi 9T Pro (raphael)"
|
||||
},
|
||||
{
|
||||
"maintainer": "easterNday",
|
||||
"maintainer_link": "https://github.com/DogDayAndroid",
|
||||
"kernel_name": "KSU_Thyme_BuildBot",
|
||||
"kernel_link": "https://github.com/DogDayAndroid/KSU_Thyme_BuildBot",
|
||||
"devices": "Xiaomi 10S"
|
||||
},
|
||||
{
|
||||
"maintainer": "tedomi2705",
|
||||
"maintainer_link": "https://github.com/tedomi2705",
|
||||
"kernel_name": "kernel_xiaomi_sdm660",
|
||||
"kernel_link": "https://github.com/tedomi2705/kernel_xiaomi_sdm660",
|
||||
"devices": "Xiaomi Redmi Note 6 Pro (tulip)"
|
||||
},
|
||||
{
|
||||
"maintainer": "RyuujiX",
|
||||
"maintainer_link": "https://github.com/RyuujiX",
|
||||
"kernel_name": "android_kernel_asus_sdm660-4.19",
|
||||
"kernel_link": "https://github.com/RyuujiX/android_kernel_asus_sdm660-4.19",
|
||||
"devices": "Asus Zenfone Max Pro M1 (X00TD)/ M2 (X01BD) (Linux 4.19)"
|
||||
},
|
||||
{
|
||||
"maintainer": "liqidecg",
|
||||
"maintainer_link": "https://github.com/liqidecg",
|
||||
"kernel_name": "android_kernel_xiaomi_sdm710",
|
||||
"kernel_link": "https://github.com/liqidecg/android_kernel_xiaomi_sdm710",
|
||||
"devices": "Mi 8 SE (sirius) (Linux 4.9)"
|
||||
},
|
||||
{
|
||||
"maintainer": "Abdelhay-Ali",
|
||||
"maintainer_link": "https://github.com/Abdelhay-Ali",
|
||||
"kernel_name": "android_kernel_huawei_hi6250",
|
||||
"kernel_link": "https://github.com/Abdelhay-Ali/android_kernel_huawei_hi6250_KernelSU",
|
||||
"devices": "Huawei P20 lite (hi6250) (Linux 4.9)"
|
||||
},
|
||||
{
|
||||
"maintainer": "reocat",
|
||||
"maintainer_link": "https://github.com/reocat",
|
||||
"kernel_name": "android_kernel_nokia_sdm660_ksu",
|
||||
"kernel_link": "https://github.com/reocat/android_kernel_nokia_sdm660_ksu",
|
||||
"devices": "Nokia 6.1 (2018)"
|
||||
},
|
||||
{
|
||||
"maintainer": "bggRGjQaUbCoE",
|
||||
"maintainer_link": "https://github.com/bggRGjQaUbCoE",
|
||||
"kernel_name": "android_kernel_samsung_sm8250",
|
||||
"kernel_link": "https://github.com/bggRGjQaUbCoE/android_kernel_samsung_sm8250",
|
||||
"devices": "Samsung Galaxy S20+ 5G (y2q)"
|
||||
},
|
||||
{
|
||||
"maintainer": "SOVIET-ANDROID",
|
||||
"maintainer_link": "https://github.com/SOVIET-ANDROID",
|
||||
"kernel_name": "kernel_xiaomi_raphael",
|
||||
"kernel_link": "https://github.com/SOVIET-ANDROID/kernel_xiaomi_raphael",
|
||||
"devices": "XK20 Pro Raphael DSP & A only SAR support "
|
||||
},
|
||||
{
|
||||
"maintainer": "AkariOficial",
|
||||
"maintainer_link": "https://github.com/AkariOficial",
|
||||
"kernel_name": "kernel_Moe_ginkgo",
|
||||
"kernel_link": "https://github.com/AkariOficial/kernel_Moe_ginkgo",
|
||||
"devices": "Xiaomi Redmi Note 8 (Ginkgo)"
|
||||
},
|
||||
{
|
||||
"maintainer": "Coconutat",
|
||||
"maintainer_link": "https://github.com/Coconutat",
|
||||
"kernel_name": "android_kernel_huawei_vtr_emui9_KernelSU",
|
||||
"kernel_link": "https://github.com/Coconutat/android_kernel_huawei_vtr_emui9_KernelSU",
|
||||
"devices": "Huawei P10,P10 Plus,Mate9,Mate9 Pro(Kirin960 Series),Stable For GSI"
|
||||
},
|
||||
{
|
||||
"maintainer": "dabao1955",
|
||||
"maintainer_link": "https://github.com/dabao1955",
|
||||
"kernel_name": "android_kernel_OPPO_PEQM00",
|
||||
"kernel_link": "https://github.com/dabao1955/android_kernel_OPPO_PEQM00",
|
||||
"devices": "MediaTek devices in the OPPO Reno series on ColorOS13.x"
|
||||
},
|
||||
{
|
||||
"maintainer": "Aflaungos",
|
||||
"maintainer_link": "https://github.com/Aflaungos",
|
||||
"kernel_name": "android_kernel_motorola_msm8998",
|
||||
"kernel_link": "https://github.com/Aflaungos/android_kernel_motorola_msm8998",
|
||||
"devices": "Moto G6 Plus (evert)"
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -16,7 +16,7 @@ Có, nhưng ở những phiên bản thử nghiệm này có thể có rất nhi
|
||||
|
||||
## KernelSU có hỗ trợ Xposed không?
|
||||
|
||||
Có, [Dreamland](https://github.com/canyie/Dreamland) và [TaiChi](https::/taichi.cool) hiện đã hoạt động được một phần nào đó. Với Lsposed, bạn có thể thử [Zygisk trên KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU)
|
||||
Có, [Dreamland](https://github.com/canyie/Dreamland) và [TaiChi](https://taichi.cool) hiện đã hoạt động được một phần nào đó. Với Lsposed, bạn có thể thử [Zygisk trên KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU)
|
||||
|
||||
## KernelSU có tương thích với Magisk không?
|
||||
|
||||
|
||||
@@ -44,8 +44,22 @@ tools/bazel build --config=fast //common:kernel_aarch64_dist
|
||||
|
||||
Nếu bạn có thể build được kernel hoàn chỉnh, thì việc tích hợp KernelSU rất dễ dàng, chạy lệnh sau tại thư mục chứa mã nguồn kernel:
|
||||
|
||||
- Latest tag(stable)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
|
||||
```
|
||||
|
||||
- main branch(dev)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
- Select tag(Such as v0.5.2)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
|
||||
```
|
||||
|
||||
Và rồi build lại, bạn sẽ có được một image chứa KernelSU
|
||||
@@ -18,10 +18,24 @@ KernelSU sử dụng kprobe để thực hiện hook kernel, nếu *kprobe* ch
|
||||
|
||||
Đầu tiên, thêm KernelSU vào mã nguồn kernel của bạn:
|
||||
|
||||
- Latest tag(stable)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
|
||||
```
|
||||
|
||||
- main branch(dev)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
- Select tag(Such as v0.5.2)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
|
||||
```
|
||||
|
||||
Sau đó, bạn nên kiểm tra xem *kprobe* có được bật trong config của bạn hay không, nếu không, vui lòng thêm các cấu hình sau vào:
|
||||
|
||||
```
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
- 模块安装目录: 都在 `/data/adb/modules`
|
||||
- systemless: 都支持通过模块的形式以 systemless 修改 /system
|
||||
- `post-fs-data.sh`: 执行时机完全一致,语义也完全一致
|
||||
- `service.sh`: 执行世纪完全一致,语义也完全一致
|
||||
- `service.sh`: 执行时机完全一致,语义也完全一致
|
||||
- `system.prop`: 完全相同
|
||||
- `sepolicy.rule`: 完全相同
|
||||
- BusyBox:脚本都在 BusyBox 中以“独立模式”运行
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
## KernelSU 是否支持 Xposed ?
|
||||
|
||||
支持。[Dreamland](https://github.com/canyie/Dreamland) 和 [TaiChi](https::/taichi.cool) 可以正常运行。LSPosed 可以在 [Zygisk on KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU) 的支持下正常运行。
|
||||
支持。[Dreamland](https://github.com/canyie/Dreamland) 和 [TaiChi](https://taichi.cool) 可以正常运行。LSPosed 可以在 [Zygisk on KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU) 的支持下正常运行。
|
||||
|
||||
## KernelSU 支持 Zygisk 吗?
|
||||
|
||||
|
||||
@@ -42,10 +42,24 @@ tools/bazel build --config=fast //common:kernel_aarch64_dist
|
||||
|
||||
## 使用 KernelSU 构建内核
|
||||
|
||||
如果您可以成功构建内核,那么构建 KernelSU 就很容易,在内核源代码根目录中运行此命令:
|
||||
如果您可以成功构建内核,那么构建 KernelSU 就很容易,根据自己的需求在内核源代码根目录中运行以下任一命令:
|
||||
|
||||
- 最新tag(稳定版本)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
|
||||
```
|
||||
|
||||
- main分支(开发版本)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
- 指定tag(比如v0.5.2)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
|
||||
```
|
||||
|
||||
然后重建内核,您将获得带有 KernelSU 的内核映像!
|
||||
|
||||
@@ -17,10 +17,24 @@ KernelSU 使用 kprobe 机制来做内核的相关 hook,如果 *kprobe* 可以
|
||||
|
||||
首先,把 KernelSU 添加到你的内核源码树,在内核的根目录执行以下命令:
|
||||
|
||||
- 最新tag(稳定版本)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
|
||||
```
|
||||
|
||||
- main分支(开发版本)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
- 指定tag(比如v0.5.2)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
|
||||
```
|
||||
|
||||
然后,你需要检查你的内核是否开启了 *kprobe* 相关的配置,如果没有开启,需要添加以下配置:
|
||||
|
||||
```
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user