├── .gitattributes ├── .github └── workflows │ └── module.yml ├── .gitignore ├── .gitmodules ├── .idea └── copyright │ ├── GPL_v3.xml │ └── profiles_settings.xml ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── module.gradle ├── module ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── cpp │ ├── CMakeLists.txt │ ├── adbd │ │ ├── adbd_preload.cpp │ │ └── adbd_wrapper_main.cpp │ ├── core │ │ ├── binder_hook.cpp │ │ ├── binder_hook.h │ │ ├── bridge_service.cpp │ │ ├── bridge_service.h │ │ ├── main.cpp │ │ ├── main.h │ │ ├── main_riru.cpp │ │ ├── main_zygisk.cpp │ │ ├── manager_process.cpp │ │ ├── manager_process.h │ │ ├── settings_process.cpp │ │ ├── settings_process.h │ │ ├── system_server.cpp │ │ └── system_server.h │ ├── include │ │ ├── config.h │ │ ├── logging.h │ │ └── zygisk.hpp │ ├── main │ │ ├── adb_root.hpp │ │ ├── main.cpp │ │ ├── sui_main.hpp │ │ └── uninstall_main.hpp │ └── util │ │ ├── android.cpp │ │ ├── app_process.cpp │ │ ├── dex_file.cpp │ │ ├── include │ │ ├── android.h │ │ ├── app_process.h │ │ ├── dex_file.h │ │ ├── memory.h │ │ ├── misc.h │ │ ├── plt.h │ │ ├── selinux.h │ │ └── socket.h │ │ ├── memory.cpp │ │ ├── misc.cpp │ │ ├── plt.c │ │ ├── selinux.cpp │ │ └── socket.cpp │ └── java │ └── rikka │ └── sui │ ├── binder │ ├── HookedBinderProxy.java │ ├── IBinderWrapper.java │ ├── Transaction.java │ └── TransactionCodeExporter.java │ ├── installer │ ├── Installer.java │ └── Uninstaller.java │ ├── manager │ ├── ManagerConstants.java │ ├── ManagerProcess.java │ └── WorkerHandler.java │ ├── model │ └── AppInfo.java │ ├── resource │ └── SuiApk.java │ ├── server │ ├── ServerConstants.java │ ├── Starter.java │ ├── SuiClientManager.java │ ├── SuiConfig.java │ ├── SuiConfigManager.java │ ├── SuiDatabase.java │ ├── SuiService.java │ ├── SuiUserServiceManager.java │ ├── bridge │ │ └── BridgeServiceClient.java │ └── userservice │ │ └── Starter.java │ ├── settings │ ├── ActivityThreadUtil.java │ ├── HandlerUtil.java │ ├── SettingsConstants.java │ ├── SettingsInstrumentation.java │ ├── SettingsProcess.java │ └── WorkerHandler.java │ ├── shell │ └── Shell.java │ ├── shortcut │ ├── ShortcutConstants.java │ └── SuiShortcut.java │ ├── systemserver │ ├── Bridge.java │ ├── BridgeService.java │ ├── PackageReceiver.java │ ├── SystemProcess.java │ └── SystemServerConstants.java │ └── util │ ├── AppNameComparator.java │ ├── BridgeServiceClient.java │ ├── BuildUtils.java │ ├── InstrumentationUtil.java │ ├── LabelComparator.java │ ├── Logger.java │ ├── MapUtil.java │ ├── OsUtils.java │ ├── ParcelFileDescriptorUtil.java │ ├── ParcelUtils.java │ ├── Preconditions.java │ ├── SQLiteDataBaseRemoteCompat.java │ ├── Unsafe.java │ └── UserHandleCompat.java ├── settings.gradle ├── template └── magisk_module │ ├── .gitattributes │ ├── CHANGELOG.md │ ├── META-INF │ └── com │ │ └── google │ │ └── android │ │ ├── update-binary │ │ └── updater-script │ ├── customize.sh │ ├── module.prop │ ├── post-fs-data.sh │ ├── post-install.example.sh │ ├── riru.sh │ ├── rish │ ├── sepolicy.rule │ ├── uninstall.sh │ ├── util_functions.sh │ └── verify.sh └── ui ├── .gitignore ├── aapt2-resources.cfg ├── build.gradle ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml ├── java └── rikka │ └── sui │ ├── SuiActivity.java │ ├── SuiRequestPermissionDialog.java │ ├── app │ ├── AppActivity.java │ └── AppFragment.java │ ├── ktx │ ├── Drawable.kt │ ├── Handler.kt │ ├── LayoutInflater.kt │ ├── Resources.kt │ ├── TextView.kt │ └── Window.kt │ ├── management │ ├── ManagementAdapter.kt │ ├── ManagementAppItemViewHolder.kt │ ├── ManagementFragment.kt │ └── ManagementViewModel.kt │ ├── model │ └── AppInfo.java │ ├── permission │ ├── ConfirmationDialog.java │ └── SystemDialogRootView.java │ ├── server │ └── SuiConfig.java │ └── util │ ├── AppIconCache.kt │ ├── AppIconUtil.java │ ├── AppInfoComparator.java │ ├── AppLabel.java │ ├── AppNameComparator.java │ ├── BridgeServiceClient.java │ ├── LabelComparator.java │ ├── Logger.java │ ├── Preconditions.java │ ├── TextUtilsCompat.java │ ├── Unsafe.java │ └── UserHandleCompat.java └── res ├── anim └── stagger_layout_animation.xml ├── animator └── alpha_animator.xml ├── color └── confirmation_dialog_button_text.xml ├── drawable ├── confirmation_dialog_background.xml ├── ic_close_24.xml ├── ic_shortcut_24.xml └── ic_su_24.xml ├── layout ├── appbar.xml ├── appbar_fragment_activity.xml ├── confirmation_dialog.xml ├── main.xml ├── management.xml └── management_app_item.xml ├── raw └── keep.xml ├── values-night └── styles.xml ├── values-v31 └── colors.xml ├── values-zh-rCN └── strings.xml ├── values-zh-rTW └── strings.xml └── values ├── colors.xml ├── dimens.xml ├── strings.xml ├── styles.xml └── themes.xml /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.bat text eol=crlf 4 | *.jar binary -------------------------------------------------------------------------------- /.github/workflows/module.yml: -------------------------------------------------------------------------------- 1 | name: Module 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths-ignore: 8 | - '**/README.md' 9 | jobs: 10 | build: 11 | runs-on: ubuntu-20.04 12 | if: ${{ !startsWith(github.event.head_commit.message, '[skip ci]') }} 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v3 17 | with: 18 | submodules: 'recursive' 19 | fetch-depth: 0 20 | - name: Setup Java 21 | uses: actions/setup-java@v3 22 | with: 23 | distribution: 'temurin' 24 | java-version: '17' 25 | cache: 'gradle' 26 | - name: Cache Gradle Dependencies 27 | uses: actions/cache@v2 28 | with: 29 | path: | 30 | ~/.gradle/caches 31 | ~/.gradle/wrapper 32 | !~/.gradle/caches/build-cache-* 33 | key: gradle-deps-module-${{ hashFiles('**/build.gradle') }} 34 | restore-keys: | 35 | gradle-deps 36 | - name: Cache Gradle Dependencies 37 | uses: actions/cache@v3 38 | with: 39 | path: | 40 | ~/.gradle/caches 41 | ~/.gradle/wrapper 42 | !~/.gradle/caches/build-cache-* 43 | key: gradle-deps-module-${{ hashFiles('**/build.gradle') }} 44 | restore-keys: | 45 | gradle-deps 46 | - name: Cache Ccache 47 | uses: actions/cache@v3 48 | with: 49 | path: ~/.ccache 50 | key: ${{ runner.os }}-ccache-cache-${{ github.sha }} 51 | restore-keys: ${{ runner.os }}-ccache-cache- 52 | - name: Install ccache 53 | run: | 54 | sudo apt-get install -y ccache 55 | ccache -o max_size=2G 56 | ccache -o hash_dir=false 57 | - name: Build with Gradle 58 | run: | 59 | mkdir -p ~/.gradle/wrapper 60 | mkdir -p ~/.gradle/caches 61 | [ $(du -s ~/.gradle/wrapper | awk '{ print $1 }') -gt 250000 ] && rm -rf ~/.gradle/wrapper/* || true 62 | find ~/.gradle/caches -exec touch -d "2 days ago" {} + || true 63 | echo 'org.gradle.caching=true' >> gradle.properties 64 | echo 'org.gradle.parallel=true' >> gradle.properties 65 | echo 'org.gradle.vfs.watch=true' >> gradle.properties 66 | echo 'org.gradle.jvmargs=-Xmx2048m' >> gradle.properties 67 | ./gradlew zipRiruDebug zipRiruRelease zipZygiskDebug zipZygiskRelease 68 | - name: Prepare artifact 69 | if: success() 70 | id: prepareArtifact 71 | run: | 72 | riruReleaseName=`ls out/sui-riru-*-release.zip | awk -F '(/|.zip)' '{print $2}'` && echo "::set-output name=riruReleaseName::$riruReleaseName" 73 | riruDebugName=`ls out/sui-riru-*-debug.zip | awk -F '(/|.zip)' '{print $2}'` && echo "::set-output name=riruDebugName::$riruDebugName" 74 | zygiskReleaseName=`ls out/sui-zygisk-*-release.zip | awk -F '(/|.zip)' '{print $2}'` && echo "::set-output name=zygiskReleaseName::$zygiskReleaseName" 75 | zygiskDebugName=`ls out/sui-zygisk-*-debug.zip | awk -F '(/|.zip)' '{print $2}'` && echo "::set-output name=zygiskDebugName::$zygiskDebugName" 76 | unzip out/sui-riru-*-release.zip -d sui-riru-release 77 | unzip out/sui-riru-*-debug.zip -d sui-riru-debug 78 | unzip out/sui-zygisk-*-release.zip -d sui-zygisk-release 79 | unzip out/sui-zygisk-*-debug.zip -d sui-zygisk-debug 80 | - name: Upload Riru release 81 | uses: actions/upload-artifact@v3 82 | with: 83 | name: ${{ steps.prepareArtifact.outputs.riruReleaseName }} 84 | path: './sui-riru-release/*' 85 | - name: Upload Riru debug 86 | uses: actions/upload-artifact@v3 87 | with: 88 | name: ${{ steps.prepareArtifact.outputs.riruDebugName }} 89 | path: './sui-riru-debug/*' 90 | - name: Upload Zygisk release 91 | uses: actions/upload-artifact@v3 92 | with: 93 | name: ${{ steps.prepareArtifact.outputs.zygiskReleaseName }} 94 | path: './sui-zygisk-release/*' 95 | - name: Upload Zygisk debug 96 | uses: actions/upload-artifact@v3 97 | with: 98 | name: ${{ steps.prepareArtifact.outputs.zygiskDebugName }} 99 | path: './sui-zygisk-debug/*' 100 | - name: Upload mappings 101 | uses: actions/upload-artifact@v3 102 | with: 103 | name: mappings 104 | path: | 105 | "module/build/outputs/mapping/release" 106 | "ui/build/outputs/mapping/release" 107 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | .idea 5 | /.idea/caches/build_file_checksums.ser 6 | /.idea/libraries 7 | /.idea/modules.xml 8 | /.idea/workspace.xml 9 | .DS_Store 10 | /build 11 | /captures 12 | /out 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "api"] 2 | path = api 3 | url = git@github.com:RikkaApps/Shizuku-API.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /.idea/copyright/GPL_v3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' version '8.0.2' apply false 3 | id 'com.android.library' version '8.0.2' apply false 4 | id 'org.jetbrains.kotlin.jvm' version '1.8.0' apply false 5 | alias libs.plugins.refine apply false 6 | } 7 | 8 | apply plugin: 'idea' 9 | 10 | idea.module { 11 | excludeDirs += file('out') 12 | resourceDirs += file('template') 13 | resourceDirs += file('scripts') 14 | } 15 | 16 | subprojects { 17 | plugins.withId("com.android.base") { 18 | plugins.apply('dev.rikka.tools.refine') 19 | 20 | android { 21 | compileSdk = 33 22 | defaultConfig { 23 | minSdk = 23 24 | targetSdk = 33 25 | } 26 | compileOptions { 27 | sourceCompatibility = JavaVersion.VERSION_17 28 | targetCompatibility = JavaVersion.VERSION_17 29 | } 30 | buildFeatures { 31 | aidl true 32 | } 33 | } 34 | } 35 | } 36 | 37 | 38 | tasks.register('clean', Delete) { 39 | delete rootProject.buildDir 40 | } 41 | 42 | ext { 43 | minSdkVersion = 23 44 | targetSdkVersion = 33 45 | api_min_sdk = 23 46 | api_target_sdk = 33 47 | } 48 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2028M" 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | android.enableResourceOptimizations=false 19 | android.enableParallelJsonGen=true 20 | android.enableNewResourceShrinker=true 21 | #android.experimental.enableNewResourceShrinker.preciseShrinking=true 22 | android.enableR8.fullMode=false 23 | android.nonTransitiveRClass=false 24 | android.nonFinalResIds=false 25 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RikkaApps/Sui/2f5fd2a04bc061eb2a8431cc3ede9066954f5a7c/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Jul 10 01:01:45 CST 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /module.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | apply from: "$rootDir/api/manifest.gradle" 3 | 4 | gitCommitId = 'git rev-parse --short HEAD'.execute([], project.rootDir).text.trim() 5 | gitCommitCount = Integer.parseInt('git rev-list --count HEAD'.execute([], project.rootDir).text.trim()) 6 | 7 | moduleLibraryName = "sui" 8 | moduleMinRiruApiVersion = 25 9 | moduleMinRiruVersionName = "v25.0.0" 10 | moduleRiruApiVersion = 25 11 | 12 | riruModuleId = "riru-sui" 13 | zygiskModuleId = "zygisk-sui" 14 | 15 | moduleName = "Sui" 16 | moduleAuthor = "Rikka" 17 | moduleDescription = "Modern superuser interface implementation." 18 | moduleVersionMinor = 5 19 | moduleVersionPatch = 1 20 | moduleVersion = "v${api_version_major}.${moduleVersionMinor}.${moduleVersionPatch}" 21 | moduleVersionCode = gitCommitCount 22 | 23 | } 24 | -------------------------------------------------------------------------------- /module/.gitignore: -------------------------------------------------------------------------------- 1 | /.externalNativeBuild 2 | /build 3 | /release -------------------------------------------------------------------------------- /module/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -repackageclasses rikka.sui 2 | 3 | -keep class rikka.sui.server.Starter { 4 | public static void main(java.lang.String[]); 5 | } 6 | 7 | -keep class rikka.sui.server.userservice.Starter { 8 | public static void main(java.lang.String[]); 9 | } 10 | 11 | -keep class rikka.sui.systemserver.SystemProcess { 12 | public static void main(java.lang.String[]); 13 | public static boolean execTransact(android.os.Binder, int, long, long, int); 14 | } 15 | 16 | -keep class rikka.sui.manager.ManagerProcess { 17 | public static void main(java.lang.String[]); 18 | } 19 | 20 | -keep class rikka.sui.settings.SettingsProcess { 21 | public static void main(java.lang.String[]); 22 | public static boolean execTransact(android.os.Binder, int, long, long, int); 23 | } 24 | 25 | -keep class rikka.sui.installer.Installer { 26 | public static void main(java.lang.String[]); 27 | } 28 | 29 | -keep class rikka.sui.installer.Uninstaller { 30 | public static void main(java.lang.String[]); 31 | } 32 | 33 | -keep class rikka.sui.shell.Shell { 34 | public static void main(java.lang.String[]); 35 | } 36 | 37 | -keepnames class * implements android.os.Parcelable 38 | 39 | -keepclassmembers class * implements android.os.Parcelable { 40 | public static final android.os.Parcelable$Creator CREATOR; 41 | } 42 | 43 | -assumenosideeffects class android.util.Log { 44 | public static *** d(...); 45 | } 46 | 47 | -assumenosideeffects class rikka.sui.util.Logger { 48 | public *** d(...); 49 | } 50 | 51 | -assumenosideeffects class rikka.shizuku.server.util.Logger { 52 | public *** d(...); 53 | } 54 | 55 | -keepattributes SourceFile,LineNumberTable 56 | -renamesourcefileattribute SourceFile 57 | 58 | -dontwarn android.** 59 | -dontwarn com.android.** 60 | -------------------------------------------------------------------------------- /module/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /module/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.1) 2 | 3 | project("sui") 4 | 5 | message("Build type: ${CMAKE_BUILD_TYPE} ${FLAVOR}") 6 | 7 | set(CMAKE_CXX_STANDARD 17) 8 | 9 | set(LINKER_FLAGS "-ffixed-x18 -Wl,--hash-style=both") 10 | set(C_FLAGS "-Werror=format -fdata-sections -ffunction-sections -fno-exceptions -fno-rtti -fno-threadsafe-statics") 11 | 12 | if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") 13 | set(C_FLAGS "${C_FLAGS} -O2 -fvisibility=hidden -fvisibility-inlines-hidden") 14 | set(LINKER_FLAGS "${LINKER_FLAGS} -Wl,-exclude-libs,ALL -Wl,--gc-sections") 15 | else () 16 | add_definitions(-DDEBUG) 17 | set(C_FLAGS "${C_FLAGS} -O0") 18 | endif () 19 | 20 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_FLAGS}") 21 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${C_FLAGS}") 22 | 23 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINKER_FLAGS}") 24 | set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_FLAGS}") 25 | 26 | find_package(cxx REQUIRED CONFIG) 27 | find_package(riru REQUIRED CONFIG) 28 | find_package(nativehelper REQUIRED CONFIG) 29 | 30 | include_directories(".") 31 | include_directories("include") 32 | include_directories("util/include") 33 | 34 | add_library(util STATIC 35 | util/misc.cpp 36 | util/dex_file.cpp 37 | util/android.cpp 38 | util/plt.c 39 | util/selinux.cpp 40 | util/memory.cpp 41 | util/app_process.cpp 42 | util/socket.cpp) 43 | target_link_libraries(util cxx::cxx log nativehelper::nativehelper_header_only) 44 | 45 | if (FLAVOR STREQUAL "riru") 46 | set(CORE_MAIN_SOURCE "main_riru.cpp") 47 | add_definitions(-DRIRU_MODULE) 48 | add_definitions(-DRIRU_MODULE_API_VERSION=${RIRU_MODULE_API_VERSION}) 49 | add_definitions(-DRIRU_MODULE_VERSION=${RIRU_MODULE_VERSION}) 50 | add_definitions(-DRIRU_MODULE_VERSION_NAME=${RIRU_MODULE_VERSION_NAME}) 51 | elseif (FLAVOR STREQUAL "zygisk") 52 | set(CORE_MAIN_SOURCE "main_zygisk.cpp") 53 | add_definitions(-DZYGISK_MODULE_ID=${ZYGISK_MODULE_ID}) 54 | else () 55 | message(FATAL_ERROR "Unknown flavor ${FLAVOR}") 56 | endif () 57 | 58 | add_library(sui SHARED 59 | core/${CORE_MAIN_SOURCE} 60 | core/main.cpp 61 | core/system_server.cpp 62 | core/binder_hook.cpp 63 | core/bridge_service.cpp 64 | core/manager_process.cpp 65 | core/settings_process.cpp) 66 | target_link_libraries(sui util cxx::cxx log riru::riru nativehelper::nativehelper_header_only) 67 | set_target_properties(sui PROPERTIES LINK_FLAGS_RELEASE -s) 68 | 69 | add_executable(libmain.so main/main.cpp) 70 | target_link_libraries(libmain.so util cxx::cxx log riru::riru nativehelper::nativehelper_header_only) 71 | set_target_properties(libmain.so PROPERTIES LINK_FLAGS_RELEASE -s) 72 | 73 | add_executable(libadbd_wrapper.so 74 | adbd/adbd_wrapper_main.cpp) 75 | target_link_libraries(libadbd_wrapper.so cxx::cxx log) 76 | set_target_properties(libadbd_wrapper.so PROPERTIES LINK_FLAGS_RELEASE -s) 77 | 78 | add_library(adbd_preload SHARED 79 | adbd/adbd_preload.cpp) 80 | target_link_libraries(adbd_preload cxx::cxx log) 81 | set_target_properties(adbd_preload PROPERTIES LINK_FLAGS_RELEASE -s) 82 | 83 | if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") 84 | add_custom_command(TARGET sui POST_BUILD 85 | COMMAND ${CMAKE_STRIP} --strip-all --remove-section=.comment "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libsui.so") 86 | 87 | add_custom_command(TARGET libmain.so POST_BUILD 88 | COMMAND ${CMAKE_STRIP} --strip-all --remove-section=.comment "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libmain.so") 89 | 90 | add_custom_command(TARGET libadbd_wrapper.so POST_BUILD 91 | COMMAND ${CMAKE_STRIP} --strip-all --remove-section=.comment "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libadbd_wrapper.so") 92 | 93 | add_custom_command(TARGET adbd_preload POST_BUILD 94 | COMMAND ${CMAKE_STRIP} --strip-all --remove-section=.comment "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libadbd_preload.so") 95 | endif () 96 | -------------------------------------------------------------------------------- /module/src/main/cpp/adbd/adbd_preload.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | extern "C" { 8 | [[gnu::constructor]] void constructor() { 9 | LOGD("preload constructor"); 10 | 11 | auto ld_preload = getenv("SUI_LD_PRELOAD_BACKUP"); 12 | if (ld_preload) { 13 | setenv("LD_PRELOAD", ld_preload, 1); 14 | } else { 15 | unsetenv("LD_PRELOAD"); 16 | } 17 | } 18 | 19 | [[gnu::visibility("default")]] [[maybe_unused]] 20 | int __android_log_is_debuggable() { // NOLINT(bugprone-reserved-identifier) 21 | return 1; 22 | } 23 | 24 | using property_get_t = int(const char *, char *, const char *); 25 | 26 | [[gnu::visibility("default")]] [[maybe_unused]] 27 | int property_get(const char *key, char *value, const char *default_value) { // NOLINT(bugprone-reserved-identifier) 28 | if (key && value && strcmp("ro.debuggable", key) == 0) { 29 | value[0] = '1'; 30 | value[1] = '\0'; 31 | return 1; 32 | } 33 | 34 | static property_get_t * original = nullptr; 35 | if (!original) { 36 | original = (property_get_t *) dlsym(RTLD_NEXT, "property_get"); 37 | } 38 | if (original) { 39 | return original(key, value, default_value); 40 | } 41 | return -1; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /module/src/main/cpp/adbd/adbd_wrapper_main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | using namespace std::literals::string_view_literals; 29 | 30 | int main(int argc, char **argv) { 31 | const char *adbd_ld_preload; 32 | const char *adbd_real; 33 | 34 | auto apex = "/apex/"sv; 35 | std::string_view argv0{argv[0]}; 36 | if (argv0.length() > apex.length() && argv0.substr(0, apex.length()) == apex) { 37 | adbd_real = "/apex/com.android.adbd/bin/adbd_real"; 38 | #ifdef __LP64__ 39 | adbd_ld_preload = "/apex/com.android.adbd/lib64/libsui_adbd_preload.so"; 40 | #else 41 | adbd_ld_preload = "/apex/com.android.adbd/lib/libsui_adbd_preload.so"; 42 | #endif 43 | } else { 44 | adbd_real = "/system/bin/adbd_real"; 45 | #ifdef __LP64__ 46 | adbd_ld_preload = "/system/lib64/libsui_adbd_preload.so"; 47 | #else 48 | adbd_ld_preload = "/system/lib/libsui_adbd_preload.so"; 49 | #endif 50 | } 51 | 52 | LOGI("adbd_main"); 53 | LOGD("adbd_real=%s", adbd_real); 54 | LOGD("adbd_ld_preload=%s", adbd_ld_preload); 55 | 56 | auto ld_preload = getenv("LD_PRELOAD"); 57 | char new_ld_preload[PATH_MAX]{}; 58 | if (ld_preload) { 59 | setenv("SUI_LD_PRELOAD_BACKUP", ld_preload, 1); 60 | snprintf(new_ld_preload, PATH_MAX, "%s:%s", adbd_ld_preload, ld_preload); 61 | } else { 62 | strcpy(new_ld_preload, adbd_ld_preload); 63 | } 64 | setenv("LD_PRELOAD", new_ld_preload, 1); 65 | LOGD("LD_PRELOAD=%s", new_ld_preload); 66 | 67 | auto root_seclabel = "--root_seclabel"sv; 68 | for (int i = 1; i < argc; ++i) { 69 | std::string_view argv_i{argv[i]}; 70 | if (argv_i.length() > root_seclabel.length() && argv_i.substr(0, root_seclabel.length()) == root_seclabel) { 71 | argv[i] = strdup("--root_seclabel=u:r:magisk:s0"); 72 | LOGD("root_seclabel -> u:r:magisk:s0"); 73 | } 74 | } 75 | 76 | return execv(adbd_real, argv); 77 | } 78 | -------------------------------------------------------------------------------- /module/src/main/cpp/core/binder_hook.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "binder_hook.h" 26 | #include "logging.h" 27 | #include 28 | #include 29 | 30 | static jmethodID original_execTransactMethodID; 31 | 32 | static const JNIInvokeInterface *old_JNIInvokeInterface = nullptr; 33 | static const JNINativeInterface *old_JNINativeInterface = nullptr; 34 | 35 | static JNIInvokeInterface *new_JNIInvokeInterface = nullptr; 36 | static JNINativeInterface *new_JNINativeInterface = nullptr; 37 | 38 | using CallBooleanMethodV_t = jboolean(JNIEnv *, jobject, jmethodID, va_list); 39 | using GetEnv_t = jint(JavaVM *, void **, jint); 40 | 41 | static GetEnv_t *old_GetEnv; 42 | static CallBooleanMethodV_t *old_CallBooleanMethodV; 43 | static BinderHook::ExecTransact_t *my_ExecTransact; 44 | 45 | using SetTableOverride_t = void(JNINativeInterface *); 46 | 47 | static jboolean new_CallBooleanMethodV(JNIEnv *env, jobject obj, jmethodID methodId, va_list args) { 48 | if (methodId == original_execTransactMethodID) { 49 | jboolean res = false; 50 | if (my_ExecTransact(&res, env, obj, args)) return res; 51 | } 52 | 53 | return old_CallBooleanMethodV(env, obj, methodId, args); 54 | } 55 | 56 | static jint new_GetEnv(JavaVM *vm, void **env, jint version) { 57 | jint res = old_GetEnv(vm, env, version); 58 | if (res == JNI_OK && env && *env) { 59 | ((JNIEnv *) *env)->functions = new_JNINativeInterface; 60 | } 61 | return res; 62 | } 63 | 64 | static void InstallDirectly(JavaVM *javaVm, JNIEnv *env) { 65 | // JavaVM 66 | old_JNIInvokeInterface = javaVm->functions; 67 | old_GetEnv = javaVm->functions->GetEnv; 68 | new_JNIInvokeInterface = new JNIInvokeInterface(); 69 | memcpy(new_JNIInvokeInterface, javaVm->functions, sizeof(JNIInvokeInterface)); 70 | new_JNIInvokeInterface->GetEnv = new_GetEnv; 71 | 72 | javaVm->functions = new_JNIInvokeInterface; 73 | 74 | // JNIEnv 75 | env->functions = new_JNINativeInterface; 76 | } 77 | 78 | static bool InstallOverrideTable() { 79 | if (android::GetApiLevel() < 26) return false; 80 | 81 | auto setTableOverride = (SetTableOverride_t *) plt_dlsym("_ZN3art9JNIEnvExt16SetTableOverrideEPK18JNINativeInterface", nullptr); 82 | if (setTableOverride != nullptr) { 83 | setTableOverride(new_JNINativeInterface); 84 | return true; 85 | } 86 | return false; 87 | } 88 | 89 | void BinderHook::Install(JavaVM *javaVm, JNIEnv *env, ExecTransact_t *callback) { 90 | my_ExecTransact = callback; 91 | 92 | // Binder 93 | ScopedLocalRef binderClass(env, env->FindClass("android/os/Binder")); 94 | original_execTransactMethodID = env->GetMethodID(binderClass.get(), "execTransact", "(IJJI)Z"); 95 | 96 | // JNIEnv 97 | old_JNINativeInterface = env->functions; 98 | old_CallBooleanMethodV = env->functions->CallBooleanMethodV; 99 | new_JNINativeInterface = new JNINativeInterface(); 100 | memcpy(new_JNINativeInterface, env->functions, sizeof(JNINativeInterface)); 101 | new_JNINativeInterface->CallBooleanMethodV = new_CallBooleanMethodV; 102 | 103 | if (InstallOverrideTable()) { 104 | LOGI("installed override table"); 105 | } else { 106 | LOGW("can't install override table"); 107 | InstallDirectly(javaVm, env); 108 | } 109 | } 110 | 111 | void BinderHook::Uninstall(JavaVM *javaVm) { 112 | javaVm->functions = old_JNIInvokeInterface; 113 | } 114 | 115 | void BinderHook::Uninstall(JNIEnv *env) { 116 | env->functions = old_JNINativeInterface; 117 | } 118 | -------------------------------------------------------------------------------- /module/src/main/cpp/core/binder_hook.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | namespace BinderHook { 25 | 26 | using ExecTransact_t = bool(jboolean *, JNIEnv *, jobject, va_list); 27 | 28 | void Install(JavaVM *javaVm, JNIEnv *env, ExecTransact_t *callback); 29 | 30 | void Uninstall(JavaVM *javaVm); 31 | 32 | void Uninstall(JNIEnv *env); 33 | } -------------------------------------------------------------------------------- /module/src/main/cpp/core/bridge_service.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | namespace BridgeService { 25 | 26 | const jint BRIDGE_TRANSACTION_CODE = 1599296841; // ('_' << 24) | ('S' << 16) | ('U' << 8) | 'I' 27 | 28 | void init(JNIEnv *env); 29 | void insertFileMonitor(JNIEnv *env, const char* packageName, const char *func, const char *path); 30 | jobjectArray requestCheckProcess(JNIEnv *env, const char* packageName); 31 | } -------------------------------------------------------------------------------- /module/src/main/cpp/core/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void UmountApexAdbd() { 9 | static bool called = false; 10 | if (called) return; 11 | 12 | if (android::GetApiLevel() >= __ANDROID_API_R__) { 13 | called = true; 14 | 15 | umount2("/apex/com.android.adbd/bin", MNT_DETACH); 16 | if (android::Has32Bit() && !android::Has64Bit()) { 17 | umount2("/apex/com.android.adbd/lib", MNT_DETACH); 18 | } 19 | if (android::Has64Bit()) { 20 | umount2("/apex/com.android.adbd/lib64", MNT_DETACH); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /module/src/main/cpp/core/main.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_H 2 | #define MAIN_H 3 | 4 | void UmountApexAdbd(); 5 | 6 | #endif //MAIN_H 7 | -------------------------------------------------------------------------------- /module/src/main/cpp/core/manager_process.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "android.h" 40 | #include "logging.h" 41 | #include "riru.h" 42 | #include "misc.h" 43 | #include "dex_file.h" 44 | #include "bridge_service.h" 45 | #include "binder_hook.h" 46 | #include "config.h" 47 | 48 | namespace Manager { 49 | 50 | static jclass mainClass = nullptr; 51 | 52 | static bool installDex(JNIEnv *env, const char *appDataDir, Dex *dexFile) { 53 | if (android::GetApiLevel() < 26) { 54 | char dexPath[PATH_MAX], oatDir[PATH_MAX]; 55 | snprintf(dexPath, PATH_MAX, "%s/sui/%s", appDataDir, DEX_NAME); 56 | snprintf(oatDir, PATH_MAX, "%s/sui/oat", appDataDir); 57 | dexFile->setPre26Paths(dexPath, oatDir); 58 | } 59 | dexFile->createClassLoader(env); 60 | 61 | mainClass = dexFile->findClass(env, MANAGER_PROCESS_CLASSNAME); 62 | if (!mainClass) { 63 | LOGE("unable to find main class"); 64 | return false; 65 | } 66 | mainClass = (jclass) env->NewGlobalRef(mainClass); 67 | 68 | auto mainMethod = env->GetStaticMethodID(mainClass, "main", "([Ljava/lang/String;)V"); 69 | if (!mainMethod) { 70 | LOGE("unable to find main method"); 71 | env->ExceptionDescribe(); 72 | env->ExceptionClear(); 73 | return false; 74 | } 75 | 76 | auto args = env->NewObjectArray(0, env->FindClass("java/lang/String"), nullptr); 77 | 78 | env->CallStaticVoidMethod(mainClass, mainMethod, args); 79 | if (env->ExceptionCheck()) { 80 | LOGE("unable to call main method"); 81 | env->ExceptionDescribe(); 82 | env->ExceptionClear(); 83 | return false; 84 | } 85 | 86 | return true; 87 | } 88 | 89 | void main(JNIEnv *env, const char *appDataDir, Dex *dexFile) { 90 | if (!dexFile->valid()) { 91 | LOGE("no dex"); 92 | return; 93 | } 94 | 95 | LOGV("main: manager"); 96 | 97 | LOGV("install dex"); 98 | 99 | if (!installDex(env, appDataDir, dexFile)) { 100 | LOGE("can't install dex"); 101 | return; 102 | } 103 | 104 | LOGV("install dex finished"); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /module/src/main/cpp/core/manager_process.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include "dex_file.h" 24 | 25 | namespace Manager { 26 | void main(JNIEnv *env, const char *appDataDir, Dex *dexFile); 27 | } 28 | -------------------------------------------------------------------------------- /module/src/main/cpp/core/settings_process.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include "dex_file.h" 24 | 25 | namespace Settings { 26 | void main(JNIEnv *env, const char *appDataDir, Dex *dexFile); 27 | } 28 | -------------------------------------------------------------------------------- /module/src/main/cpp/core/system_server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include "dex_file.h" 24 | 25 | namespace SystemServer { 26 | void main(JNIEnv *env, Dex *dexFile); 27 | } 28 | -------------------------------------------------------------------------------- /module/src/main/cpp/include/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | #pragma once 21 | 22 | #define MANAGER_APPLICATION_ID "com.android.systemui" 23 | #define SETTINGS_APPLICATION_ID "com.android.settings" 24 | 25 | #define DEX_NAME "sui.dex" 26 | #define SYSTEM_PROCESS_CLASSNAME "rikka/sui/systemserver/SystemProcess" 27 | #define MANAGER_PROCESS_CLASSNAME "rikka/sui/manager/ManagerProcess" 28 | #define SETTINGS_PROCESS_CLASSNAME "rikka/sui/settings/SettingsProcess" 29 | -------------------------------------------------------------------------------- /module/src/main/cpp/include/logging.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | #ifndef _LOGGING_H 21 | #define _LOGGING_H 22 | 23 | #include 24 | #include "android/log.h" 25 | 26 | #ifndef LOG_TAG 27 | #define LOG_TAG "Sui" 28 | #endif 29 | 30 | #ifdef DEBUG 31 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) 32 | #else 33 | #define LOGD(...) 34 | #endif 35 | #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) 36 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 37 | #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) 38 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 39 | #define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)) 40 | 41 | #endif // _LOGGING_H 42 | -------------------------------------------------------------------------------- /module/src/main/cpp/main/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "sui_main.hpp" 5 | #include "adb_root.hpp" 6 | #include "uninstall_main.hpp" 7 | 8 | using main_func = int (*)(int, char **); 9 | 10 | static main_func applet_func[] = {sui_main, adb_root_main, uninstall_main, nullptr }; 11 | 12 | static const char* applet_names[] = {"sui", "adb_root", "uninstall", nullptr }; 13 | 14 | int main(int argc, char **argv) { 15 | auto uid = getuid(); 16 | if (uid != 0) { 17 | exit(EXIT_FAILURE); 18 | } 19 | 20 | auto base = basename(argv[0]); 21 | for (int i = 0; applet_names[i]; ++i) { 22 | if (strcmp(base, applet_names[i]) == 0) { 23 | return applet_func[i](argc, argv); 24 | } 25 | } 26 | return 1; 27 | } 28 | -------------------------------------------------------------------------------- /module/src/main/cpp/main/sui_main.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | /* 29 | * argv[1]: path of the module, such as /data/adb/modules/zygisk-sui 30 | */ 31 | static int sui_main(int argc, char **argv) { 32 | LOGI("Sui starter begin: %s", argv[1]); 33 | 34 | if (daemon(false, false) != 0) { 35 | PLOGE("daemon"); 36 | return EXIT_FAILURE; 37 | } 38 | 39 | wait_for_zygote(); 40 | 41 | if (access("/data/adb/sui", F_OK) != 0) { 42 | mkdir("/data/adb/sui", 0600); 43 | } 44 | chmod("/data/adb/sui", 0600); 45 | chown("/data/adb/sui", 0, 0); 46 | 47 | auto root_path = argv[1]; 48 | 49 | char dex_path[PATH_MAX]{0}; 50 | strcpy(dex_path, root_path); 51 | strcat(dex_path, "/sui.dex"); 52 | 53 | app_process(dex_path, root_path, "rikka.sui.server.Starter", "sui"); 54 | 55 | return EXIT_SUCCESS; 56 | } 57 | -------------------------------------------------------------------------------- /module/src/main/cpp/main/uninstall_main.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | /* 28 | * argv[1]: path of the module, such as /data/adb/modules/zygisk-sui 29 | */ 30 | static int uninstall_main(int argc, char **argv) { 31 | LOGI("Sui uninstaller begin: %s", argv[1]); 32 | 33 | auto root_path = argv[1]; 34 | 35 | char dex_path[PATH_MAX]{0}; 36 | strcpy(dex_path, root_path); 37 | strcat(dex_path, "/sui.dex"); 38 | 39 | if (copyfile(dex_path, "/dev/sui.dex") != 0) { 40 | PLOGE("copyfile"); 41 | return EXIT_FAILURE; 42 | } 43 | 44 | if (daemon(false, false) != 0) { 45 | PLOGE("daemon"); 46 | return EXIT_FAILURE; 47 | } 48 | 49 | wait_for_zygote(); 50 | 51 | app_process("/dev/sui.dex", "/dev", "rikka.sui.installer.Uninstaller", "sui_uninstaller"); 52 | unlink("/dev/sui.dex"); 53 | 54 | return EXIT_SUCCESS; 55 | } 56 | -------------------------------------------------------------------------------- /module/src/main/cpp/util/android.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | #include 21 | 22 | namespace android { 23 | 24 | static int apiLevel = 0; 25 | static int previewApiLevel = 0; 26 | static int8_t has32Bit = -1; 27 | static int8_t has64Bit = -1; 28 | 29 | int GetApiLevel() { 30 | if (apiLevel > 0) return apiLevel; 31 | 32 | char buf[PROP_VALUE_MAX + 1]; 33 | if (__system_property_get("ro.build.version.sdk", buf) > 0) 34 | apiLevel = atoi(buf); 35 | 36 | return apiLevel; 37 | } 38 | 39 | int GetPreviewApiLevel() { 40 | if (previewApiLevel > 0) return previewApiLevel; 41 | 42 | char buf[PROP_VALUE_MAX + 1]; 43 | if (__system_property_get("ro.build.version.preview_sdk", buf) > 0) 44 | previewApiLevel = atoi(buf); 45 | 46 | return previewApiLevel; 47 | } 48 | 49 | bool Has32Bit() { 50 | if (has32Bit != -1) return has32Bit == 1; 51 | 52 | char buf[PROP_VALUE_MAX + 1]; 53 | has32Bit = __system_property_get("ro.product.cpu.abilist32", buf) > 0 ? 1 : 0; 54 | return has32Bit; 55 | } 56 | 57 | bool Has64Bit() { 58 | if (has64Bit != -1) return has64Bit == 1; 59 | 60 | char buf[PROP_VALUE_MAX + 1]; 61 | has64Bit = __system_property_get("ro.product.cpu.abilist64", buf) > 0 ? 1 : 0; 62 | return has64Bit; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /module/src/main/cpp/util/include/android.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | #pragma once 21 | 22 | namespace android { 23 | 24 | int GetApiLevel(); 25 | 26 | int GetPreviewApiLevel(); 27 | 28 | bool Has32Bit(); 29 | 30 | bool Has64Bit(); 31 | } 32 | -------------------------------------------------------------------------------- /module/src/main/cpp/util/include/app_process.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2022 Sui Contributors 18 | */ 19 | 20 | #ifndef APP_PROCESS_H 21 | #define APP_PROCESS_H 22 | 23 | void app_process(const char *dex_path, const char *files_path, const char *main_class, const char *process_name); 24 | 25 | void wait_for_zygote(); 26 | 27 | #endif //APP_PROCESS_H 28 | -------------------------------------------------------------------------------- /module/src/main/cpp/util/include/dex_file.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | #pragma once 21 | 22 | class Buffer { 23 | 24 | protected: 25 | uint8_t *bytes_ = nullptr; 26 | size_t size_ = 0; 27 | private: 28 | bool is_mmap_ = 0; 29 | 30 | public: 31 | Buffer() = default; 32 | 33 | Buffer(const char *path); 34 | 35 | Buffer(int fd, size_t size); 36 | 37 | ~Buffer(); 38 | 39 | uint8_t *data() const; 40 | 41 | size_t size() const; 42 | 43 | int writeToFile(const char *path, mode_t mode); 44 | }; 45 | 46 | class Dex { 47 | 48 | private: 49 | Buffer buffer_; 50 | char *pre26DexPath_ = nullptr; 51 | char *pre26OptDir_ = nullptr; 52 | 53 | jclass dexClassLoaderClass = nullptr; 54 | jmethodID findClassMethod = nullptr; 55 | jobject dexClassLoader = nullptr; 56 | 57 | public: 58 | Dex(int fd, size_t size); 59 | 60 | Dex(const char *path); 61 | 62 | ~Dex(); 63 | 64 | void destroy(JNIEnv *env); 65 | 66 | void createClassLoader(JNIEnv *env); 67 | 68 | jclass findClass(JNIEnv *env, const char *name); 69 | 70 | void setPre26Paths(const char *dexPath, const char *optDir); 71 | 72 | bool valid(); 73 | 74 | private: 75 | 76 | void createInMemoryDexClassLoader(JNIEnv *env); 77 | 78 | void createDexClassLoader(JNIEnv *env, const char *path, const char *optDir); 79 | 80 | void copyDexToFile(const char *dexPath); 81 | 82 | }; 83 | -------------------------------------------------------------------------------- /module/src/main/cpp/util/include/memory.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMORY_H 2 | #define MEMORY_H 3 | 4 | int CreateSharedMem(const char *name, size_t size); 5 | int SetSharedMemProt(int fd, int prot); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /module/src/main/cpp/util/include/misc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | #ifndef _MISC_H 21 | #define _MISC_H 22 | 23 | #include 24 | #include 25 | 26 | int mkdirs(const char *pathname, mode_t mode); 27 | int ensure_dir(const char *path, mode_t mode); 28 | int copyfileat(int src_path_fd, const char *src_path, int dst_path_fd, const char *dst_path); 29 | int copyfile(const char *src_path, const char *dst_path); 30 | ssize_t read_eintr(int fd, void *out, size_t len); 31 | int read_full(int fd, void *buf, size_t count); 32 | int write_full(int fd, const void *buf, size_t count); 33 | 34 | using foreach_proc_function = bool(pid_t); 35 | void foreach_proc(foreach_proc_function *func); 36 | 37 | #endif // _MISC_H 38 | -------------------------------------------------------------------------------- /module/src/main/cpp/util/include/plt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #define PLT_CHECK_PLT_APP ((unsigned short) 0x1u) 12 | #define PLT_CHECK_PLT_ALL ((unsigned short) 0x2u) 13 | #define PLT_CHECK_NAME ((unsigned short) 0x4u) 14 | #define PLT_CHECK_SYM_ONE ((unsigned short) 0x8u) 15 | 16 | typedef struct Symbol { 17 | unsigned short check; 18 | unsigned short size; 19 | size_t total; 20 | ElfW(Addr) *symbol_plt; 21 | ElfW(Addr) *symbol_sym; 22 | const char *symbol_name; 23 | char **names; 24 | } Symbol; 25 | 26 | int dl_iterate_phdr_symbol(Symbol *symbol); 27 | 28 | void *plt_dlsym(const char *name, size_t *total); 29 | 30 | #ifdef __cplusplus 31 | } 32 | #endif 33 | -------------------------------------------------------------------------------- /module/src/main/cpp/util/include/selinux.h: -------------------------------------------------------------------------------- 1 | #ifndef _SELINUX_H 2 | #define _SELINUX_H 3 | 4 | bool init_selinux(); 5 | void freecon(char *con); 6 | int getfilecon_raw(const char *path, char **con); 7 | int setfilecon_raw(const char *path, const char *context); 8 | int selinux_check_access(const char * scon, const char * tcon, const char *tclass, const char *perm, void *auditdata); 9 | 10 | #endif // _SELINUX_H 11 | -------------------------------------------------------------------------------- /module/src/main/cpp/util/include/socket.h: -------------------------------------------------------------------------------- 1 | #ifndef SOCKET_H 2 | #define SOCKET_H 3 | 4 | socklen_t setup_sockaddr(struct sockaddr_un *sun, const char *name); 5 | int set_socket_timeout(int fd, long sec); 6 | int send_fd(int sockfd, int fd); 7 | int recv_fd(int sockfd); 8 | int read_int(int fd); 9 | void write_int(int fd, int val); 10 | #endif // SOCKET_H 11 | -------------------------------------------------------------------------------- /module/src/main/cpp/util/memory.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* 8 | * ashmem_create_region - creates a new ashmem region and returns the file 9 | * descriptor, or <0 on error 10 | * 11 | * `name' is an optional label to give the region (visible in /proc/pid/maps) 12 | * `size' is the size of the region, in page-aligned bytes 13 | */ 14 | using ashmem_create_region_t = int(const char *name, size_t size); 15 | 16 | static ashmem_create_region_t *ashmem_create_region = nullptr; 17 | 18 | using ashmem_set_prot_region_t = int(int fd, int prot); 19 | 20 | static ashmem_set_prot_region_t *ashmem_set_prot_region = nullptr; 21 | 22 | static void Init() { 23 | static bool init = false; 24 | if (init) return; 25 | 26 | #ifdef __LP64__ 27 | auto handle = dlopen("/system/lib64/libcutils.so", 0); 28 | #else 29 | auto handle = dlopen("/system/lib/libcutils.so", 0); 30 | #endif 31 | 32 | if (handle) { 33 | ashmem_create_region = (ashmem_create_region_t *) dlsym(handle, "ashmem_create_region"); 34 | ashmem_set_prot_region = (ashmem_set_prot_region_t *) dlsym(handle, "ashmem_set_prot_region"); 35 | } 36 | 37 | init = true; 38 | } 39 | 40 | int CreateSharedMem(const char *name, size_t size) { 41 | Init(); 42 | if (!ashmem_create_region) return -1; 43 | 44 | int ret; 45 | if ((ret = ashmem_create_region(name, size)) >= 0) { 46 | return ret; 47 | } 48 | PLOGE("ashmem_create_region %s", name); 49 | return ret; 50 | } 51 | 52 | int SetSharedMemProt(int fd, int prot) { 53 | Init(); 54 | if (!ashmem_create_region) return 0; 55 | 56 | int ret; 57 | if ((ret = ashmem_set_prot_region(fd, prot)) == -1) { 58 | PLOGE("ashmem_set_prot_region"); 59 | } 60 | return ret; 61 | } 62 | -------------------------------------------------------------------------------- /module/src/main/cpp/util/selinux.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define INITCONTEXTLEN 255 12 | 13 | void freecon(char *con) { 14 | free(con); 15 | } 16 | 17 | int getfilecon_raw(const char *path, char **context) { 18 | char *buf; 19 | ssize_t size; 20 | ssize_t ret; 21 | 22 | size = INITCONTEXTLEN + 1; 23 | buf = static_cast(malloc(size)); 24 | if (!buf) 25 | return -1; 26 | memset(buf, 0, size); 27 | 28 | ret = getxattr(path, XATTR_NAME_SELINUX, buf, size - 1); 29 | if (ret < 0 && errno == ERANGE) { 30 | char *newbuf; 31 | 32 | size = getxattr(path, XATTR_NAME_SELINUX, nullptr, 0); 33 | if (size < 0) 34 | goto out; 35 | 36 | size++; 37 | newbuf = static_cast(realloc(buf, size)); 38 | if (!newbuf) 39 | goto out; 40 | 41 | buf = newbuf; 42 | memset(buf, 0, size); 43 | ret = getxattr(path, XATTR_NAME_SELINUX, buf, size - 1); 44 | } 45 | out: 46 | if (ret == 0) { 47 | /* Re-map empty attribute values to errors. */ 48 | errno = ENOTSUP; 49 | ret = -1; 50 | } 51 | if (ret < 0) 52 | free(buf); 53 | else 54 | *context = buf; 55 | return ret; 56 | } 57 | 58 | int setfilecon_raw(const char *path, const char *context) { 59 | int rc = setxattr(path, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0); 60 | if (rc < 0 && errno == ENOTSUP) { 61 | char *ccontext = nullptr; 62 | int err = errno; 63 | if ((getfilecon_raw(path, &ccontext) >= 0) && 64 | (strcmp(context, ccontext) == 0)) { 65 | rc = 0; 66 | } else { 67 | errno = err; 68 | } 69 | freecon(ccontext); 70 | } 71 | return rc; 72 | } 73 | 74 | using selinux_check_access_t = int(const char *, const char *, const char *, const char *, void *); 75 | selinux_check_access_t *selinux_check_access_func = nullptr; 76 | 77 | int selinux_check_access(const char *scon, const char *tcon, const char *tclass, const char *perm, void *auditdata) { 78 | if (selinux_check_access_func) { 79 | return selinux_check_access_func(scon, tcon, tclass, perm, auditdata); 80 | } 81 | return 0; 82 | } 83 | 84 | #ifdef __LP64__ 85 | static constexpr const char *libselinux = "/system/lib64/libselinux.so"; 86 | #else 87 | static constexpr const char *libselinux = "/system/lib/libselinux.so"; 88 | #endif 89 | 90 | bool init_selinux() { 91 | if (access(libselinux, F_OK) != 0) { 92 | return false; 93 | } 94 | 95 | void *handle = dlopen(libselinux, RTLD_LAZY | RTLD_LOCAL); 96 | if (!handle) return false; 97 | 98 | selinux_check_access_func = (selinux_check_access_t *) dlsym(handle, "selinux_check_access"); 99 | return selinux_check_access_func != nullptr; 100 | } 101 | -------------------------------------------------------------------------------- /module/src/main/cpp/util/socket.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | static size_t socket_len(sockaddr_un *sun) { 14 | if (sun->sun_path[0]) 15 | return sizeof(sa_family_t) + strlen(sun->sun_path) + 1; 16 | else 17 | return sizeof(sa_family_t) + strlen(sun->sun_path + 1) + 1; 18 | } 19 | 20 | socklen_t setup_sockaddr(sockaddr_un *sun, const char *name) { 21 | memset(sun, 0, sizeof(*sun)); 22 | sun->sun_family = AF_UNIX; 23 | strcpy(sun->sun_path + 1, name); 24 | return socket_len(sun); 25 | } 26 | 27 | int set_socket_timeout(int fd, long sec) { 28 | struct timeval timeout{}; 29 | timeout.tv_sec = sec; 30 | timeout.tv_usec = 0; 31 | 32 | if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof(timeout)) < 0) { 33 | return -1; 34 | } 35 | 36 | if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *) &timeout, sizeof(timeout)) < 0) { 37 | return -1; 38 | } 39 | return 0; 40 | } 41 | 42 | ssize_t xsendmsg(int sockfd, const struct msghdr *msg, int flags) { 43 | int sent = sendmsg(sockfd, msg, flags); 44 | if (sent < 0) { 45 | PLOGE("sendmsg"); 46 | } 47 | return sent; 48 | } 49 | 50 | ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags) { 51 | int rec = recvmsg(sockfd, msg, flags); 52 | if (rec < 0) { 53 | PLOGE("recvmsg"); 54 | } 55 | return rec; 56 | } 57 | 58 | int send_fds(int sockfd, void *cmsgbuf, size_t bufsz, const int *fds, int cnt) { 59 | iovec iov = { 60 | .iov_base = &cnt, 61 | .iov_len = sizeof(cnt), 62 | }; 63 | msghdr msg = { 64 | .msg_iov = &iov, 65 | .msg_iovlen = 1, 66 | }; 67 | 68 | if (cnt) { 69 | msg.msg_control = cmsgbuf; 70 | msg.msg_controllen = bufsz; 71 | cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 72 | cmsg->cmsg_len = CMSG_LEN(sizeof(int) * cnt); 73 | cmsg->cmsg_level = SOL_SOCKET; 74 | cmsg->cmsg_type = SCM_RIGHTS; 75 | 76 | memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * cnt); 77 | } 78 | 79 | return xsendmsg(sockfd, &msg, 0); 80 | } 81 | 82 | int send_fd(int sockfd, int fd) { 83 | if (fd < 0) { 84 | return send_fds(sockfd, nullptr, 0, nullptr, 0); 85 | } 86 | char cmsgbuf[CMSG_SPACE(sizeof(int))]; 87 | return send_fds(sockfd, cmsgbuf, sizeof(cmsgbuf), &fd, 1); 88 | } 89 | 90 | void *recv_fds(int sockfd, char *cmsgbuf, size_t bufsz, int cnt) { 91 | iovec iov = { 92 | .iov_base = &cnt, 93 | .iov_len = sizeof(cnt), 94 | }; 95 | msghdr msg = { 96 | .msg_iov = &iov, 97 | .msg_iovlen = 1, 98 | .msg_control = cmsgbuf, 99 | .msg_controllen = bufsz 100 | }; 101 | 102 | xrecvmsg(sockfd, &msg, MSG_WAITALL); 103 | cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 104 | 105 | if (msg.msg_controllen != bufsz || 106 | cmsg == nullptr || 107 | cmsg->cmsg_len != CMSG_LEN(sizeof(int) * cnt) || 108 | cmsg->cmsg_level != SOL_SOCKET || 109 | cmsg->cmsg_type != SCM_RIGHTS) { 110 | return nullptr; 111 | } 112 | 113 | return CMSG_DATA(cmsg); 114 | } 115 | 116 | int recv_fd(int sockfd) { 117 | char cmsgbuf[CMSG_SPACE(sizeof(int))]; 118 | 119 | void *data = recv_fds(sockfd, cmsgbuf, sizeof(cmsgbuf), 1); 120 | if (data == nullptr) 121 | return -1; 122 | 123 | int result; 124 | memcpy(&result, data, sizeof(int)); 125 | return result; 126 | } 127 | 128 | int read_int(int fd) { 129 | int val; 130 | if (read_full(fd, &val, sizeof(val)) != 0) 131 | return -1; 132 | return val; 133 | } 134 | 135 | void write_int(int fd, int val) { 136 | if (fd < 0) return; 137 | write_full(fd, &val, sizeof(val)); 138 | } 139 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/binder/HookedBinderProxy.java: -------------------------------------------------------------------------------- 1 | package rikka.sui.binder; 2 | 3 | import android.os.IBinder; 4 | 5 | public interface HookedBinderProxy extends IBinder { 6 | 7 | T getOriginal(); 8 | 9 | boolean isTransactionReplaced(int code); 10 | } 11 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/binder/IBinderWrapper.java: -------------------------------------------------------------------------------- 1 | package rikka.sui.binder; 2 | 3 | import android.os.IBinder; 4 | import android.os.IInterface; 5 | import android.os.Parcel; 6 | import android.os.RemoteException; 7 | 8 | import androidx.annotation.NonNull; 9 | 10 | import java.io.FileDescriptor; 11 | 12 | public class IBinderWrapper implements IBinder { 13 | 14 | private final IBinder mOriginal; 15 | 16 | public IBinderWrapper(IBinder original) { 17 | this.mOriginal = original; 18 | } 19 | 20 | public IBinder getOriginal() { 21 | return mOriginal; 22 | } 23 | 24 | @Override 25 | public boolean transact(int code, @NonNull Parcel data, Parcel reply, int flags) throws RemoteException { 26 | return mOriginal.transact(code, data, reply, flags); 27 | } 28 | 29 | @Override 30 | public String getInterfaceDescriptor() throws RemoteException { 31 | return mOriginal.getInterfaceDescriptor(); 32 | } 33 | 34 | @Override 35 | public boolean pingBinder() { 36 | return mOriginal.pingBinder(); 37 | } 38 | 39 | @Override 40 | public boolean isBinderAlive() { 41 | return mOriginal.isBinderAlive(); 42 | } 43 | 44 | @Override 45 | public IInterface queryLocalInterface(@NonNull String descriptor) { 46 | return mOriginal.queryLocalInterface(descriptor); 47 | } 48 | 49 | @Override 50 | public void dump(@NonNull FileDescriptor fd, String[] args) throws RemoteException { 51 | mOriginal.dump(fd, args); 52 | } 53 | 54 | @Override 55 | public void dumpAsync(@NonNull FileDescriptor fd, String[] args) throws RemoteException { 56 | mOriginal.dumpAsync(fd, args); 57 | } 58 | 59 | @Override 60 | public void linkToDeath(@NonNull DeathRecipient recipient, int flags) throws RemoteException { 61 | mOriginal.linkToDeath(recipient, flags); 62 | } 63 | 64 | @Override 65 | public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) { 66 | return mOriginal.unlinkToDeath(recipient, flags); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/binder/Transaction.java: -------------------------------------------------------------------------------- 1 | package rikka.sui.binder; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.METHOD) 10 | public @interface Transaction { 11 | } 12 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/installer/Installer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.installer; 21 | 22 | import android.content.pm.ApplicationInfo; 23 | 24 | import java.io.File; 25 | import java.io.FileWriter; 26 | import java.io.IOException; 27 | import java.util.Locale; 28 | 29 | import rikka.hidden.compat.PackageManagerApis; 30 | 31 | public class Installer { 32 | 33 | private static void saveApplicationInfoToFile(String path, String packageName, String name) throws IOException { 34 | ApplicationInfo ai = PackageManagerApis.getApplicationInfoNoThrow(packageName, 0, 0); 35 | if (ai == null) { 36 | System.out.println("! Can't fetch application info for package " + packageName); 37 | return; 38 | } 39 | int uid = ai.uid; 40 | String processName = ai.processName != null ? ai.processName : packageName; 41 | System.out.println("- " + name + ": uid=" + uid + ", processName=" + processName); 42 | 43 | File file = new File(path, packageName); 44 | if (!file.exists() && !file.createNewFile()) { 45 | System.out.println("! Can't create " + file); 46 | return; 47 | } 48 | 49 | FileWriter writer = new FileWriter(file); 50 | writer.write(String.format(Locale.ENGLISH, "%d\n%s", uid, processName)); 51 | writer.flush(); 52 | writer.close(); 53 | } 54 | 55 | public static void main(String[] args) throws IOException { 56 | System.out.println("- AppProcess: main"); 57 | saveApplicationInfoToFile(args[0], "com.android.systemui", "SystemUI"); 58 | saveApplicationInfoToFile(args[0], "com.android.settings", "Settings"); 59 | System.out.println("- AppProcess: exit"); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/installer/Uninstaller.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2022 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.installer; 21 | 22 | import android.annotation.TargetApi; 23 | import android.content.pm.IPackageManager; 24 | import android.content.pm.IShortcutService; 25 | import android.content.pm.IShortcutServiceV31; 26 | import android.os.Build; 27 | import android.os.Handler; 28 | import android.os.IUserManager; 29 | import android.os.Looper; 30 | import android.os.RemoteException; 31 | import android.os.ServiceManager; 32 | import android.system.ErrnoException; 33 | import android.system.Os; 34 | import android.util.Log; 35 | 36 | import java.io.IOException; 37 | import java.util.ArrayList; 38 | import java.util.List; 39 | 40 | import dev.rikka.tools.refine.Refine; 41 | import rikka.sui.shortcut.ShortcutConstants; 42 | 43 | @TargetApi(Build.VERSION_CODES.O) 44 | public class Uninstaller { 45 | 46 | private static final String TAG = "SuiUninstaller"; 47 | 48 | private static void removeShortcuts() throws InterruptedException, RemoteException { 49 | IShortcutService shortcutService = null; 50 | IUserManager userManager = null; 51 | 52 | while (true) { 53 | //noinspection ConstantConditions 54 | if (shortcutService == null) { 55 | shortcutService = IShortcutService.Stub.asInterface(ServiceManager.getService("shortcut")); 56 | } 57 | if (userManager == null) { 58 | userManager = IUserManager.Stub.asInterface(ServiceManager.getService("user")); 59 | } 60 | if (shortcutService != null 61 | && userManager != null 62 | && userManager.isUserUnlocked(0)) { 63 | break; 64 | } 65 | 66 | //noinspection BusyWait 67 | Thread.sleep(1000); 68 | Log.v(TAG, "wait 1s"); 69 | } 70 | 71 | List list = new ArrayList<>(); 72 | list.add(ShortcutConstants.SHORTCUT_ID); 73 | 74 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 75 | Refine.unsafeCast(shortcutService).removeDynamicShortcuts( 76 | "com.android.settings", list, 0); 77 | } else { 78 | shortcutService.removeDynamicShortcuts( 79 | "com.android.settings", list, 0); 80 | } 81 | } 82 | 83 | public static void main(String[] args) throws IOException, ErrnoException { 84 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { 85 | return; 86 | } 87 | 88 | Log.i(TAG, "main"); 89 | 90 | //noinspection deprecation 91 | Os.setuid(1000); 92 | 93 | if (Looper.getMainLooper() == null) { 94 | Looper.prepareMainLooper(); 95 | } 96 | 97 | new Handler(Looper.getMainLooper()).post(() -> { 98 | try { 99 | removeShortcuts(); 100 | } catch (Throwable e) { 101 | Log.e(TAG, Log.getStackTraceString(e)); 102 | } 103 | 104 | Log.i(TAG, "exit"); 105 | System.exit(0); 106 | }); 107 | 108 | Looper.loop(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/manager/ManagerConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.manager; 21 | 22 | import rikka.sui.util.Logger; 23 | 24 | public class ManagerConstants { 25 | 26 | public static final Logger LOGGER = new Logger("SuiManager"); 27 | } 28 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/manager/WorkerHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.manager; 21 | 22 | import android.os.Handler; 23 | import android.os.HandlerThread; 24 | 25 | public class WorkerHandler { 26 | 27 | private static final HandlerThread HANDLER_THREAD; 28 | private static final Handler HANDLER; 29 | 30 | static { 31 | HANDLER_THREAD = new HandlerThread("SuiManager"); 32 | HANDLER_THREAD.start(); 33 | HANDLER = new Handler(HANDLER_THREAD.getLooper()); 34 | } 35 | 36 | public static Handler get() { 37 | return HANDLER; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/model/AppInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.model; 21 | 22 | import android.content.pm.PackageInfo; 23 | import android.os.Parcel; 24 | import android.os.Parcelable; 25 | import android.util.Log; 26 | 27 | public class AppInfo implements Parcelable { 28 | 29 | public PackageInfo packageInfo; 30 | public int flags; 31 | public CharSequence label = null; 32 | 33 | public AppInfo() { 34 | } 35 | 36 | protected AppInfo(Parcel in) { 37 | packageInfo = in.readParcelable(PackageInfo.class.getClassLoader()); 38 | flags = in.readInt(); 39 | } 40 | 41 | public static final Creator CREATOR = new Creator() { 42 | @Override 43 | public AppInfo createFromParcel(Parcel in) { 44 | return new AppInfo(in); 45 | } 46 | 47 | @Override 48 | public AppInfo[] newArray(int size) { 49 | return new AppInfo[size]; 50 | } 51 | }; 52 | 53 | @Override 54 | public int describeContents() { 55 | return 0; 56 | } 57 | 58 | @Override 59 | public void writeToParcel(Parcel dest, int flags) { 60 | dest.writeParcelable(packageInfo, flags); 61 | dest.writeInt(this.flags); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/server/ServerConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.server; 21 | 22 | import rikka.sui.util.Logger; 23 | 24 | public class ServerConstants { 25 | 26 | public static final Logger LOGGER = new Logger("SuiServer"); 27 | 28 | public static final int BINDER_TRANSACTION_getApplications = 10001; 29 | public static final int BINDER_TRANSACTION_showManagement = 10002; 30 | public static final int BINDER_TRANSACTION_openApk = 10003; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/server/Starter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.server; 21 | 22 | import static rikka.sui.server.ServerConstants.LOGGER; 23 | 24 | import android.content.Context; 25 | import android.ddm.DdmHandleAppName; 26 | import android.os.ServiceManager; 27 | 28 | import java.util.Objects; 29 | 30 | public class Starter { 31 | 32 | private static void waitSystemService(String name) { 33 | while (ServiceManager.getService(name) == null) { 34 | try { 35 | LOGGER.i("service " + name + " is not started, wait 1s."); 36 | Thread.sleep(1000); 37 | } catch (InterruptedException e) { 38 | LOGGER.w(e.getMessage(), e); 39 | } 40 | } 41 | } 42 | 43 | public static void main(String[] args) { 44 | String filesPath = null; 45 | 46 | for (String arg : args) { 47 | if (arg.equals("--debug")) { 48 | DdmHandleAppName.setAppName("sui", 0); 49 | } else if (arg.startsWith("--files-path=")) { 50 | filesPath = arg.substring("--files-path=".length()); 51 | SuiUserServiceManager.setStartDex(filesPath + "/sui.dex"); 52 | } 53 | } 54 | 55 | Objects.requireNonNull(filesPath, "--files-path not set"); 56 | 57 | waitSystemService("package"); 58 | waitSystemService("activity"); 59 | waitSystemService(Context.USER_SERVICE); 60 | waitSystemService(Context.APP_OPS_SERVICE); 61 | 62 | SuiService.main(filesPath); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/server/SuiClientManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.server; 21 | 22 | import rikka.shizuku.server.ClientManager; 23 | 24 | public class SuiClientManager extends ClientManager { 25 | 26 | public SuiClientManager(SuiConfigManager configManager) { 27 | super(configManager); 28 | } 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/server/SuiConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.server; 21 | 22 | import androidx.annotation.NonNull; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | import rikka.shizuku.server.ConfigPackageEntry; 28 | 29 | public class SuiConfig { 30 | 31 | public static final int LATEST_VERSION = 1; 32 | 33 | public static final int FLAG_ALLOWED = 1 << 1; 34 | public static final int FLAG_DENIED = 1 << 2; 35 | public static final int FLAG_HIDDEN = 1 << 3; 36 | public static final int MASK_PERMISSION = FLAG_ALLOWED | FLAG_DENIED | FLAG_HIDDEN; 37 | 38 | public int version = LATEST_VERSION; 39 | 40 | public List packages = new ArrayList<>(); 41 | 42 | public static class PackageEntry extends ConfigPackageEntry { 43 | 44 | public final int uid; 45 | 46 | public int flags; 47 | 48 | public PackageEntry(int uid, int flags) { 49 | this.uid = uid; 50 | this.flags = flags; 51 | } 52 | 53 | public boolean isAllowed() { 54 | return (flags & FLAG_ALLOWED) != 0; 55 | } 56 | 57 | public boolean isDenied() { 58 | return (flags & FLAG_DENIED) != 0; 59 | } 60 | 61 | public boolean isHidden() { 62 | return (flags & FLAG_HIDDEN) != 0; 63 | } 64 | } 65 | 66 | public SuiConfig() { 67 | } 68 | 69 | public SuiConfig(@NonNull List packages) { 70 | this.version = LATEST_VERSION; 71 | this.packages = packages; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/server/SuiConfigManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.server; 21 | 22 | import androidx.annotation.Nullable; 23 | 24 | import java.util.List; 25 | 26 | import rikka.shizuku.server.ConfigManager; 27 | 28 | public class SuiConfigManager extends ConfigManager { 29 | 30 | public static SuiConfig load() { 31 | SuiConfig config = SuiDatabase.readConfig(); 32 | if (config == null) { 33 | LOGGER.i("failed to read database, starting empty"); 34 | return new SuiConfig(); 35 | } 36 | return config; 37 | } 38 | 39 | private static SuiConfigManager instance; 40 | 41 | public static SuiConfigManager getInstance() { 42 | if (instance == null) { 43 | instance = new SuiConfigManager(); 44 | } 45 | return instance; 46 | } 47 | 48 | 49 | private final SuiConfig config; 50 | 51 | public SuiConfigManager() { 52 | this.config = load(); 53 | } 54 | 55 | private SuiConfig.PackageEntry findLocked(int uid) { 56 | for (SuiConfig.PackageEntry entry : config.packages) { 57 | if (uid == entry.uid) { 58 | return entry; 59 | } 60 | } 61 | return null; 62 | } 63 | 64 | @Nullable 65 | public SuiConfig.PackageEntry find(int uid) { 66 | synchronized (this) { 67 | return findLocked(uid); 68 | } 69 | } 70 | 71 | @Override 72 | public void update(int uid, List packages, int mask, int values) { 73 | update(uid, mask, values); 74 | } 75 | 76 | public void update(int uid, int mask, int values) { 77 | synchronized (this) { 78 | SuiConfig.PackageEntry entry = findLocked(uid); 79 | if (entry == null) { 80 | entry = new SuiConfig.PackageEntry(uid, mask & values); 81 | config.packages.add(entry); 82 | } else { 83 | int newValue = (entry.flags & ~mask) | (mask & values); 84 | if (newValue == entry.flags) { 85 | return; 86 | } 87 | entry.flags = newValue; 88 | } 89 | SuiDatabase.updateUid(uid, entry.flags); 90 | } 91 | } 92 | 93 | @Override 94 | public void remove(int uid) { 95 | synchronized (this) { 96 | SuiConfig.PackageEntry entry = findLocked(uid); 97 | if (entry == null) { 98 | return; 99 | } 100 | config.packages.remove(entry); 101 | SuiDatabase.removeUid(uid); 102 | } 103 | } 104 | 105 | public boolean isHidden(int uid) { 106 | SuiConfig.PackageEntry entry = find(uid); 107 | if (entry == null) { 108 | return false; 109 | } 110 | return (entry.flags & SuiConfig.FLAG_HIDDEN) != 0; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/server/SuiDatabase.java: -------------------------------------------------------------------------------- 1 | package rikka.sui.server; 2 | 3 | import android.content.ContentValues; 4 | import android.database.Cursor; 5 | import android.database.sqlite.SQLiteDatabase; 6 | 7 | import androidx.annotation.Nullable; 8 | 9 | import java.io.File; 10 | 11 | import rikka.sui.server.SuiConfig.PackageEntry; 12 | import rikka.sui.util.SQLiteDataBaseRemoteCompat; 13 | 14 | public class SuiDatabase { 15 | 16 | private SuiDatabase() { 17 | } 18 | 19 | static { 20 | DATABASE_PATH = (new File("/data/adb/sui/sui.db")).getPath(); 21 | } 22 | 23 | private static final String DATABASE_PATH; 24 | private static final String UID_CONFIG_TABLE = "uid_configs"; 25 | private static SQLiteDatabase databaseInternal; 26 | 27 | private static SQLiteDatabase createDatabase(boolean allowRetry) { 28 | SQLiteDatabase database; 29 | try { 30 | database = SQLiteDataBaseRemoteCompat.openDatabase(DATABASE_PATH, null); 31 | database.execSQL("CREATE TABLE IF NOT EXISTS uid_configs(uid INTEGER PRIMARY KEY, flags INTEGER);"); 32 | } catch (Throwable e) { 33 | ServerConstants.LOGGER.e(e, "create database"); 34 | if (allowRetry && (new File(DATABASE_PATH)).delete()) { 35 | ServerConstants.LOGGER.i("delete database and retry"); 36 | database = createDatabase(false); 37 | } else { 38 | database = null; 39 | } 40 | } 41 | 42 | return database; 43 | } 44 | 45 | private static SQLiteDatabase getDatabase() { 46 | if (databaseInternal == null) { 47 | databaseInternal = createDatabase(true); 48 | } 49 | return databaseInternal; 50 | } 51 | 52 | @Nullable 53 | public static SuiConfig readConfig() { 54 | SQLiteDatabase database = getDatabase(); 55 | if (database == null) { 56 | return null; 57 | } 58 | 59 | try (Cursor cursor = database.query(UID_CONFIG_TABLE, (String[]) null, (String) null, (String[]) null, (String) null, (String) null, (String) null, (String) null)) { 60 | if (cursor == null) { 61 | return null; 62 | } 63 | SuiConfig res = new SuiConfig(); 64 | int cursorIndexOfUid = cursor.getColumnIndexOrThrow("uid"); 65 | int cursorIndexOfFlags = cursor.getColumnIndexOrThrow("flags"); 66 | if (cursor.moveToFirst()) { 67 | do { 68 | res.packages.add(new PackageEntry(cursor.getInt(cursorIndexOfUid), cursor.getInt(cursorIndexOfFlags))); 69 | } while (cursor.moveToNext()); 70 | } 71 | return res; 72 | } 73 | } 74 | 75 | public static void updateUid(int uid, int flags) { 76 | SQLiteDatabase database = getDatabase(); 77 | if (database == null) { 78 | return; 79 | } 80 | 81 | ContentValues values = new ContentValues(); 82 | values.put("uid", uid); 83 | values.put("flags", flags); 84 | String selection = "uid=?"; 85 | String[] selectionArgs = new String[]{String.valueOf(uid)}; 86 | if (database.update(UID_CONFIG_TABLE, values, selection, selectionArgs) <= 0) { 87 | database.insertWithOnConflict(UID_CONFIG_TABLE, (String) null, values, SQLiteDatabase.CONFLICT_IGNORE); 88 | } 89 | } 90 | 91 | public static void removeUid(int uid) { 92 | SQLiteDatabase database = getDatabase(); 93 | if (database == null) { 94 | return; 95 | } 96 | 97 | String selection = "uid=?"; 98 | String[] selectionArgs = new String[]{String.valueOf(uid)}; 99 | database.delete(UID_CONFIG_TABLE, selection, selectionArgs); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/server/SuiUserServiceManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.server; 21 | 22 | import android.os.Build; 23 | 24 | import java.io.File; 25 | import java.util.Locale; 26 | 27 | import rikka.shizuku.server.UserServiceManager; 28 | 29 | public class SuiUserServiceManager extends UserServiceManager { 30 | 31 | public static final String USER_SERVICE_CMD_DEBUG; 32 | 33 | private static final String USER_SERVICE_CMD_FORMAT = "(CLASSPATH='%s' %s%s /system/bin " + 34 | "--nice-name='%s' %s " + 35 | "--token='%s' --package='%s' --class='%s' --uid=%d%s)&"; 36 | 37 | static { 38 | int sdk = Build.VERSION.SDK_INT; 39 | if (sdk >= 30) { 40 | USER_SERVICE_CMD_DEBUG = "-Xcompiler-option" + " --debuggable" + 41 | " -XjdwpProvider:adbconnection" + 42 | " -XjdwpOptions:suspend=n,server=y"; 43 | } else if (sdk >= 28) { 44 | USER_SERVICE_CMD_DEBUG = "-Xcompiler-option" + " --debuggable" + 45 | " -XjdwpProvider:internal" + 46 | " -XjdwpOptions:transport=dt_android_adb,suspend=n,server=y"; 47 | } else { 48 | USER_SERVICE_CMD_DEBUG = "-Xcompiler-option" + " --debuggable" + 49 | " -agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y"; 50 | } 51 | } 52 | 53 | private static String dexPath; 54 | 55 | public static void setStartDex(String path) { 56 | SuiUserServiceManager.dexPath = path; 57 | } 58 | 59 | @Override 60 | public String getUserServiceStartCmd(rikka.shizuku.server.UserServiceRecord record, String key, String token, String packageName, String classname, String processNameSuffix, int callingUid, boolean use32Bits, boolean debug) { 61 | String appProcess = "/system/bin/app_process"; 62 | if (use32Bits && new File("/system/bin/app_process32").exists()) { 63 | appProcess = "/system/bin/app_process32"; 64 | } 65 | String processName = String.format("%s:%s", packageName, processNameSuffix); 66 | return String.format(Locale.ENGLISH, USER_SERVICE_CMD_FORMAT, dexPath, appProcess, 67 | debug ? (" " + SuiUserServiceManager.USER_SERVICE_CMD_DEBUG) : "", 68 | processName, "rikka.sui.server.userservice.Starter", 69 | token, packageName, classname, callingUid, debug ? (" " + "--debug-name=" + processName) : ""); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/server/userservice/Starter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.server.userservice; 21 | 22 | import static rikka.shizuku.ShizukuApiConstants.USER_SERVICE_ARG_TOKEN; 23 | 24 | import android.os.Bundle; 25 | import android.os.IBinder; 26 | import android.os.Looper; 27 | import android.os.Parcel; 28 | import android.os.ServiceManager; 29 | import android.util.Log; 30 | import android.util.Pair; 31 | 32 | import moe.shizuku.server.IShizukuService; 33 | import rikka.shizuku.server.UserService; 34 | 35 | public class Starter { 36 | 37 | private static final String TAG = "SuiUserServiceStarter"; 38 | private static final int BRIDGE_TRANSACTION_CODE = ('_' << 24) | ('S' << 16) | ('U' << 8) | 'I'; 39 | private static final String BRIDGE_SERVICE_DESCRIPTOR = "android.app.IActivityManager"; 40 | private static final String BRIDGE_SERVICE_NAME = "activity"; 41 | private static final int BRIDGE_ACTION_GET_BINDER = 2; 42 | 43 | public static void main(String[] args) { 44 | if (Looper.getMainLooper() == null) { 45 | Looper.prepareMainLooper(); 46 | } 47 | 48 | IBinder service; 49 | String token; 50 | 51 | UserService.setTag(TAG); 52 | Pair result = UserService.create(args); 53 | 54 | if (result == null) { 55 | System.exit(1); 56 | return; 57 | } 58 | 59 | service = result.first; 60 | token = result.second; 61 | 62 | if (!sendBinder(service, token)) { 63 | System.exit(1); 64 | } 65 | 66 | Looper.loop(); 67 | System.exit(0); 68 | 69 | Log.i(TAG, "service exited"); 70 | } 71 | 72 | private static IBinder requestBinderFromBridge() { 73 | IBinder binder = ServiceManager.getService(BRIDGE_SERVICE_NAME); 74 | if (binder == null) return null; 75 | 76 | Parcel data = Parcel.obtain(); 77 | Parcel reply = Parcel.obtain(); 78 | try { 79 | data.writeInterfaceToken(BRIDGE_SERVICE_DESCRIPTOR); 80 | data.writeInt(BRIDGE_ACTION_GET_BINDER); 81 | binder.transact(BRIDGE_TRANSACTION_CODE, data, reply, 0); 82 | reply.readException(); 83 | IBinder received = reply.readStrongBinder(); 84 | if (received != null) { 85 | return received; 86 | } 87 | } catch (Throwable e) { 88 | e.printStackTrace(); 89 | } finally { 90 | data.recycle(); 91 | reply.recycle(); 92 | } 93 | return null; 94 | } 95 | 96 | private static boolean sendBinder(IBinder binder, String token) { 97 | IShizukuService shizukuService = IShizukuService.Stub.asInterface(requestBinderFromBridge()); 98 | if (shizukuService == null) { 99 | return false; 100 | } 101 | 102 | Bundle data = new Bundle(); 103 | data.putString(USER_SERVICE_ARG_TOKEN, token); 104 | try { 105 | shizukuService.attachUserService(binder, data); 106 | } catch (Throwable e) { 107 | Log.w(TAG, Log.getStackTraceString(e)); 108 | return false; 109 | } 110 | return true; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/settings/HandlerUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.settings; 21 | 22 | import android.annotation.SuppressLint; 23 | import android.os.Handler; 24 | 25 | import androidx.annotation.NonNull; 26 | 27 | import java.lang.reflect.Field; 28 | 29 | @SuppressWarnings("JavaReflectionMemberAccess") 30 | @SuppressLint("DiscouragedPrivateApi") 31 | public class HandlerUtil { 32 | 33 | private static Field callbackField; 34 | 35 | public static void init() throws ReflectiveOperationException { 36 | callbackField = Handler.class.getDeclaredField("mCallback"); 37 | callbackField.setAccessible(true); 38 | } 39 | 40 | public static Handler.Callback getCallback(@NonNull Handler handler) { 41 | try { 42 | return (Handler.Callback) callbackField.get(handler); 43 | } catch (IllegalAccessException e) { 44 | return null; 45 | } 46 | } 47 | 48 | public static void setCallback(@NonNull Handler handler, Handler.Callback callback){ 49 | try { 50 | callbackField.set(handler, callback); 51 | } catch (IllegalAccessException ignored) { 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/settings/SettingsConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.settings; 21 | 22 | import rikka.sui.util.Logger; 23 | 24 | public class SettingsConstants { 25 | 26 | public static final Logger LOGGER = new Logger("SuiSettings"); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/settings/WorkerHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.settings; 21 | 22 | import android.os.Handler; 23 | import android.os.HandlerThread; 24 | 25 | public class WorkerHandler { 26 | 27 | private static final HandlerThread HANDLER_THREAD; 28 | private static final Handler HANDLER; 29 | 30 | static { 31 | HANDLER_THREAD = new HandlerThread("SuiSettings"); 32 | HANDLER_THREAD.start(); 33 | HANDLER = new Handler(HANDLER_THREAD.getLooper()); 34 | } 35 | 36 | public static Handler get() { 37 | return HANDLER; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/shell/Shell.java: -------------------------------------------------------------------------------- 1 | package rikka.sui.shell; 2 | 3 | import android.content.pm.PackageManager; 4 | import android.os.Handler; 5 | import android.os.IBinder; 6 | import android.os.Looper; 7 | import android.system.Os; 8 | import android.text.TextUtils; 9 | 10 | import java.io.File; 11 | 12 | import rikka.rish.Rish; 13 | import rikka.rish.RishConfig; 14 | import rikka.shizuku.Shizuku; 15 | import rikka.shizuku.ShizukuApiConstants; 16 | import rikka.sui.Sui; 17 | 18 | public class Shell extends Rish { 19 | 20 | @Override 21 | public void requestPermission(Runnable onGrantedRunnable) { 22 | if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) { 23 | onGrantedRunnable.run(); 24 | } else if (Shizuku.shouldShowRequestPermissionRationale()) { 25 | System.err.println("Permission denied"); 26 | System.err.flush(); 27 | System.exit(1); 28 | } else { 29 | Shizuku.addRequestPermissionResultListener(new Shizuku.OnRequestPermissionResultListener() { 30 | @Override 31 | public void onRequestPermissionResult(int requestCode, int grantResult) { 32 | Shizuku.removeRequestPermissionResultListener(this); 33 | 34 | if (grantResult == PackageManager.PERMISSION_GRANTED) { 35 | onGrantedRunnable.run(); 36 | } else { 37 | System.err.println("Permission denied"); 38 | System.err.flush(); 39 | System.exit(1); 40 | } 41 | } 42 | }); 43 | Shizuku.requestPermission(0); 44 | } 45 | } 46 | 47 | public static void main(String[] args) { 48 | String packageName; 49 | if (Os.getuid() == 2000) { 50 | packageName = "com.android.shell"; 51 | } else { 52 | packageName = System.getenv("RISH_APPLICATION_ID"); 53 | if (TextUtils.isEmpty(packageName) || "PKG".equals(packageName)) { 54 | System.err.println("RISH_APPLICATION_ID is not set, set this environment variable to the id of current application (package name)"); 55 | System.err.flush(); 56 | System.exit(1); 57 | } 58 | } 59 | 60 | String libPath = System.getProperty("sui.library.path"); 61 | if (libPath != null) { 62 | RishConfig.setLibraryPath(new File(libPath).getAbsolutePath()); 63 | 64 | } 65 | 66 | if (Looper.getMainLooper() == null) { 67 | Looper.prepareMainLooper(); 68 | } 69 | 70 | Handler handler = new Handler(Looper.getMainLooper()); 71 | 72 | try { 73 | if (!Sui.init(packageName)) { 74 | System.err.println("Unable to acquire the binder of Sui. Make sure Sui is installed and the current application is not hidden in Sui."); 75 | System.err.flush(); 76 | System.exit(1); 77 | } 78 | } catch (Throwable tr) { 79 | tr.printStackTrace(System.err); 80 | System.err.flush(); 81 | System.exit(1); 82 | } 83 | 84 | IBinder binder = Shizuku.getBinder(); 85 | 86 | handler.post(() -> { 87 | RishConfig.init(binder, ShizukuApiConstants.BINDER_DESCRIPTOR, 30000); 88 | Shizuku.onBinderReceived(binder, packageName); 89 | Shizuku.addBinderReceivedListenerSticky(() -> { 90 | int version = Shizuku.getVersion(); 91 | if (version < 12) { 92 | System.err.println("Rish requires server 12 (running " + version + ")"); 93 | System.err.flush(); 94 | System.exit(1); 95 | } 96 | new Shell().start(args); 97 | }); 98 | }); 99 | 100 | Looper.loop(); 101 | System.exit(0); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/shortcut/ShortcutConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.shortcut; 21 | 22 | import rikka.sui.util.Logger; 23 | 24 | public class ShortcutConstants { 25 | 26 | public static final Logger LOGGER = new Logger("SuiShortcut"); 27 | 28 | public static final String SHORTCUT_ID = "rikka.sui.shortcut.SUI"; 29 | public static final String SHORTCUT_EXTRA = "rikka.sui.extra.SUI"; 30 | } 31 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/systemserver/Bridge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.systemserver; 21 | 22 | import android.content.Intent; 23 | 24 | import moe.shizuku.server.IShizukuService; 25 | 26 | import static rikka.sui.systemserver.SystemServerConstants.LOGGER; 27 | 28 | public class Bridge { 29 | 30 | public static void dispatchPackageChanged(Intent intent) { 31 | IShizukuService service = BridgeService.get(); 32 | if (service == null) { 33 | LOGGER.d("binder is null"); 34 | return; 35 | } 36 | 37 | try { 38 | service.dispatchPackageChanged(intent); 39 | } catch (Throwable e) { 40 | LOGGER.w(e, "dispatchPackageChanged"); 41 | } 42 | } 43 | 44 | public static boolean isHidden(int uid) { 45 | IShizukuService service = BridgeService.get(); 46 | if (service == null) { 47 | LOGGER.d("binder is null"); 48 | return false; 49 | } 50 | 51 | try { 52 | return service.isHidden(uid); 53 | } catch (Throwable e) { 54 | LOGGER.w(e, "isHidden"); 55 | return false; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/systemserver/PackageReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.systemserver; 21 | 22 | import static rikka.sui.systemserver.SystemServerConstants.LOGGER; 23 | 24 | import android.app.ActivityThread; 25 | import android.content.BroadcastReceiver; 26 | import android.content.Context; 27 | import android.content.ContextHidden; 28 | import android.content.Intent; 29 | import android.content.IntentFilter; 30 | import android.net.Uri; 31 | import android.os.Handler; 32 | import android.os.Looper; 33 | import android.os.UserHandleHidden; 34 | 35 | import dev.rikka.tools.refine.Refine; 36 | 37 | public class PackageReceiver { 38 | 39 | private static final BroadcastReceiver RECEIVER = new BroadcastReceiver() { 40 | @Override 41 | public void onReceive(Context context, Intent intent) { 42 | Uri uri = intent.getData(); 43 | String pkgName = (uri != null) ? uri.getSchemeSpecificPart() : null; 44 | int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); 45 | LOGGER.d("%s: %s (%d)", intent.getAction(), pkgName, uid); 46 | Bridge.dispatchPackageChanged(intent); 47 | } 48 | }; 49 | 50 | public static void register() { 51 | ActivityThread activityThread = ActivityThread.currentActivityThread(); 52 | if (activityThread == null) { 53 | LOGGER.w("ActivityThread is null"); 54 | return; 55 | } 56 | 57 | IntentFilter intentFilter = new IntentFilter(); 58 | intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 59 | intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 60 | intentFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); 61 | intentFilter.addDataScheme("package"); 62 | 63 | Handler handler = new Handler(Looper.getMainLooper()); 64 | 65 | try { 66 | Refine.unsafeCast(ActivityThread.currentActivityThread().getSystemContext()) 67 | .registerReceiverAsUser( 68 | RECEIVER, 69 | Refine.unsafeCast(UserHandleHidden.ALL), 70 | intentFilter, 71 | null, 72 | handler 73 | ); 74 | LOGGER.d("register package receiver"); 75 | } catch (Throwable e) { 76 | LOGGER.w("registerReceiver failed", e); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/systemserver/SystemProcess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.systemserver; 21 | 22 | import static rikka.sui.systemserver.SystemServerConstants.LOGGER; 23 | 24 | import android.os.Binder; 25 | import android.os.IBinder; 26 | import android.os.Parcel; 27 | 28 | import androidx.annotation.NonNull; 29 | 30 | import java.util.Arrays; 31 | 32 | import rikka.sui.util.ParcelUtils; 33 | 34 | public final class SystemProcess { 35 | 36 | private static final BridgeService SERVICE = new BridgeService(); 37 | 38 | private static boolean execActivityTransaction(@NonNull Binder binder, int code, Parcel data, Parcel reply, int flags) { 39 | return SERVICE.onTransact(code, data, reply, flags); 40 | } 41 | 42 | public static boolean execTransact(@NonNull Binder binder, int code, long dataObj, long replyObj, int flags) { 43 | if (!SERVICE.isServiceTransaction(code)) { 44 | return false; 45 | } 46 | 47 | Parcel data = ParcelUtils.fromNativePointer(dataObj); 48 | Parcel reply = ParcelUtils.fromNativePointer(replyObj); 49 | 50 | if (data == null) { 51 | return false; 52 | } 53 | 54 | boolean res; 55 | try { 56 | res = execActivityTransaction(binder, code, data, reply, flags); 57 | } catch (Exception e) { 58 | if ((flags & IBinder.FLAG_ONEWAY) != 0) { 59 | LOGGER.w(e, "Caught a Exception from the binder stub implementation."); 60 | } else { 61 | if (reply != null) { 62 | reply.setDataPosition(0); 63 | reply.writeException(e); 64 | } 65 | } 66 | res = false; 67 | } finally { 68 | data.setDataPosition(0); 69 | if (reply != null) reply.setDataPosition(0); 70 | } 71 | 72 | if (res) { 73 | data.recycle(); 74 | if (reply != null) reply.recycle(); 75 | } 76 | 77 | return res; 78 | } 79 | 80 | public static void main(String[] args) { 81 | LOGGER.d("main: %s", Arrays.toString(args)); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/systemserver/SystemServerConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.systemserver; 21 | 22 | import rikka.sui.util.Logger; 23 | 24 | public class SystemServerConstants { 25 | 26 | public static final Logger LOGGER = new Logger("SuiSystemServer"); 27 | } 28 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/util/AppNameComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.util; 21 | 22 | import android.os.Process; 23 | 24 | import java.util.Comparator; 25 | 26 | public class AppNameComparator implements Comparator { 27 | 28 | private final LabelComparator mLabelComparator; 29 | private final InfoProvider mInfoProvider; 30 | 31 | public interface InfoProvider { 32 | CharSequence getTitle(T item); 33 | String getPackageName(T item); 34 | int getUserId(T item); 35 | } 36 | 37 | public AppNameComparator(InfoProvider infoProvider) { 38 | mInfoProvider = infoProvider; 39 | mLabelComparator = new LabelComparator(); 40 | } 41 | 42 | @Override 43 | public int compare(T a, T b) { 44 | // Order by the title in the current locale 45 | int result = mLabelComparator.compare( 46 | mInfoProvider.getTitle(a).toString(), 47 | mInfoProvider.getTitle(b).toString()); 48 | 49 | if (result != 0) { 50 | return result; 51 | } 52 | 53 | // If labels are same, compare component names 54 | result = mInfoProvider.getPackageName(a).compareTo(mInfoProvider.getPackageName(b)); 55 | if (result != 0) { 56 | return result; 57 | } 58 | 59 | if (Process.myUserHandle().hashCode() == mInfoProvider.getUserId(a)) { 60 | return -1; 61 | } else { 62 | int aUserId = mInfoProvider.getUserId(a); 63 | int bUserId = mInfoProvider.getUserId(b); 64 | return Integer.compare(aUserId, bUserId); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/util/BuildUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.util; 21 | 22 | import android.os.Build; 23 | 24 | public class BuildUtils { 25 | 26 | private static final int SDK = Build.VERSION.SDK_INT; 27 | 28 | private static final int PREVIEW_SDK = SDK >= 23 ? Build.VERSION.PREVIEW_SDK_INT : 0; 29 | 30 | public static boolean atLeast31() { 31 | return SDK >= 31; 32 | } 33 | 34 | public static boolean atLeast30() { 35 | return SDK >= 30; 36 | } 37 | 38 | public static boolean atLeast29() { 39 | return SDK >= 29; 40 | } 41 | 42 | public static boolean atLeast28() { 43 | return SDK >= 28; 44 | } 45 | 46 | public static boolean atLeast26() { 47 | return SDK >= 26; 48 | } 49 | 50 | public static boolean atLeast24() { 51 | return SDK >= 24; 52 | } 53 | 54 | public static boolean atLeast23() { 55 | return SDK >= 23; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/util/LabelComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package rikka.sui.util; 17 | 18 | import java.text.Collator; 19 | import java.util.Comparator; 20 | 21 | /** 22 | * Extension of {@link java.text.Collator} with special handling for digits. Used for comparing 23 | * user visible labels. 24 | */ 25 | public class LabelComparator implements Comparator { 26 | 27 | private final Collator mCollator = Collator.getInstance(); 28 | 29 | @Override 30 | public int compare(String titleA, String titleB) { 31 | // Ensure that we de-prioritize any titles that don't start with a 32 | // linguistic letter or digit 33 | boolean aStartsWithLetter = (titleA.length() > 0) && 34 | Character.isLetterOrDigit(titleA.codePointAt(0)); 35 | boolean bStartsWithLetter = (titleB.length() > 0) && 36 | Character.isLetterOrDigit(titleB.codePointAt(0)); 37 | if (aStartsWithLetter && !bStartsWithLetter) { 38 | return -1; 39 | } else if (!aStartsWithLetter && bStartsWithLetter) { 40 | return 1; 41 | } 42 | 43 | // Order by the title in the current locale 44 | return mCollator.compare(titleA, titleB); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/util/MapUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.util; 21 | 22 | import java.util.Map; 23 | 24 | public class MapUtil { 25 | 26 | public interface Func { 27 | V call(); 28 | } 29 | 30 | public static V getOrPut(Map map, K key, Func func) { 31 | if (map.containsKey(key)) { 32 | return map.get(key); 33 | } 34 | V value = func.call(); 35 | map.put(key, value); 36 | return value; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/util/OsUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.util; 21 | 22 | import android.os.SELinux; 23 | 24 | public class OsUtils { 25 | 26 | private static final int UID = android.system.Os.getuid(); 27 | private static final int PID = android.system.Os.getpid(); 28 | private static final String SELINUX_CONTEXT; 29 | 30 | static { 31 | String context; 32 | try { 33 | context = SELinux.getContext(); 34 | } catch (Throwable tr) { 35 | context =null; 36 | } 37 | SELINUX_CONTEXT = context; 38 | } 39 | 40 | 41 | public static int getUid() { 42 | return UID; 43 | } 44 | 45 | public static int getPid() { 46 | return PID; 47 | } 48 | 49 | public static String getSELinuxContext() { 50 | return SELINUX_CONTEXT; 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/util/ParcelFileDescriptorUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.util; 21 | 22 | import android.os.ParcelFileDescriptor; 23 | import android.util.Log; 24 | 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | import java.io.OutputStream; 28 | 29 | public class ParcelFileDescriptorUtil { 30 | 31 | public static ParcelFileDescriptor pipeFrom(InputStream inputStream) throws IOException { 32 | ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); 33 | ParcelFileDescriptor readSide = pipe[0]; 34 | ParcelFileDescriptor writeSide = pipe[1]; 35 | 36 | new TransferThread(inputStream, new ParcelFileDescriptor.AutoCloseOutputStream(writeSide)) 37 | .start(); 38 | 39 | return readSide; 40 | } 41 | 42 | public static ParcelFileDescriptor pipeTo(OutputStream outputStream) throws IOException { 43 | ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); 44 | ParcelFileDescriptor readSide = pipe[0]; 45 | ParcelFileDescriptor writeSide = pipe[1]; 46 | 47 | new TransferThread(new ParcelFileDescriptor.AutoCloseInputStream(readSide), outputStream) 48 | .start(); 49 | 50 | return writeSide; 51 | } 52 | 53 | static class TransferThread extends Thread { 54 | final InputStream mIn; 55 | final OutputStream mOut; 56 | 57 | TransferThread(InputStream in, OutputStream out) { 58 | super("ParcelFileDescriptor Transfer Thread"); 59 | mIn = in; 60 | mOut = out; 61 | setDaemon(true); 62 | } 63 | 64 | @Override 65 | public void run() { 66 | byte[] buf = new byte[8192]; 67 | int len; 68 | 69 | try { 70 | while ((len = mIn.read(buf)) > 0) { 71 | mOut.write(buf, 0, len); 72 | mOut.flush(); 73 | } 74 | } catch (IOException e) { 75 | Log.e("TransferThread", Log.getStackTraceString(e)); 76 | } finally { 77 | try { 78 | mIn.close(); 79 | } catch (IOException e) { 80 | e.printStackTrace(); 81 | } 82 | try { 83 | mOut.close(); 84 | } catch (IOException e) { 85 | e.printStackTrace(); 86 | } 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/util/ParcelUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.util; 21 | 22 | import android.os.Build; 23 | import android.os.Parcel; 24 | 25 | import java.lang.reflect.Method; 26 | 27 | public class ParcelUtils { 28 | 29 | public static String readInterfaceDescriptor(Parcel parcel) { 30 | parcel.readInt(); 31 | if (Build.VERSION.SDK_INT >= 29) { 32 | parcel.readInt(); 33 | } 34 | if (Build.VERSION.SDK_INT >= 30) { 35 | parcel.readInt(); 36 | } 37 | return parcel.readString(); 38 | } 39 | 40 | private static Method obtainMethod; 41 | 42 | public static Parcel fromNativePointer(long ptr) { 43 | if (ptr == 0) return null; 44 | 45 | if (obtainMethod == null) { 46 | try { 47 | //noinspection JavaReflectionMemberAccess 48 | obtainMethod = Parcel.class.getDeclaredMethod("obtain", long.class); 49 | obtainMethod.setAccessible(true); 50 | } catch (Throwable e) { 51 | throw new RuntimeException(e); 52 | } 53 | } 54 | 55 | try { 56 | return (Parcel) obtainMethod.invoke(null, ptr); 57 | } catch (Throwable e) { 58 | throw new RuntimeException(e); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/util/SQLiteDataBaseRemoteCompat.java: -------------------------------------------------------------------------------- 1 | package rikka.sui.util; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.database.DatabaseErrorHandler; 5 | import android.database.sqlite.SQLiteDatabase; 6 | import android.os.Build; 7 | import android.util.Log; 8 | 9 | import java.io.File; 10 | 11 | public class SQLiteDataBaseRemoteCompat { 12 | 13 | private static final String TAG = "SQLiteDataBaseRemoteCompat"; 14 | 15 | @SuppressLint("WrongConstant") 16 | public static SQLiteDatabase openDatabase(String path, DatabaseErrorHandler errorHandler) { 17 | File file = new File(path); 18 | 19 | int openFlags = SQLiteDatabase.OPEN_READWRITE 20 | | SQLiteDatabase.CREATE_IF_NECESSARY 21 | | SQLiteDatabase.NO_LOCALIZED_COLLATORS 22 | | SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING; 23 | 24 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { 25 | SQLiteDatabase.OpenParams.Builder params = new SQLiteDatabase.OpenParams.Builder() 26 | .addOpenFlags(openFlags) 27 | .setErrorHandler(sqLiteDatabase -> Log.w(TAG, "database corrupted")); 28 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 29 | params.setSynchronousMode("NORMAL"); 30 | } 31 | return SQLiteDatabase.openDatabase(file, params.build()); 32 | } else { 33 | return SQLiteDatabase.openDatabase( 34 | path, null, 35 | openFlags, 36 | errorHandler 37 | ); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/util/Unsafe.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.util; 21 | 22 | public class Unsafe { 23 | @SuppressWarnings("unchecked") 24 | public static T unsafeCast(Object object) { 25 | return (T) object; 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /module/src/main/java/rikka/sui/util/UserHandleCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.util; 21 | 22 | public class UserHandleCompat { 23 | 24 | public static final int PER_USER_RANGE = 100000; 25 | 26 | public static int getUserId(int uid) { 27 | return uid / PER_USER_RANGE; 28 | } 29 | 30 | public static int getAppId(int uid) { 31 | return uid % PER_USER_RANGE; 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | mavenLocal() 7 | } 8 | } 9 | dependencyResolutionManagement { 10 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 11 | repositories { 12 | google() 13 | mavenCentral() 14 | mavenLocal() 15 | } 16 | versionCatalogs { 17 | libs { 18 | version('hidden-api', '4.1.0') 19 | library('hidden-compat', 'dev.rikka.hidden', 'compat').versionRef('hidden-api') 20 | library('hidden-stub', 'dev.rikka.hidden', 'stub').versionRef('hidden-api') 21 | 22 | version('refine', '4.3.0') 23 | library('refine-runtime', 'dev.rikka.tools.refine', 'runtime').versionRef('refine') 24 | library('refine-annotation', 'dev.rikka.tools.refine', 'annotation').versionRef('refine') 25 | library('refine-annotation-processor', 'dev.rikka.tools.refine', 'annotation-processor').versionRef('refine') 26 | plugin('refine', 'dev.rikka.tools.refine').versionRef('refine') 27 | } 28 | } 29 | } 30 | 31 | include ':module', ':ui' 32 | 33 | import org.apache.tools.ant.DirectoryScanner 34 | 35 | DirectoryScanner.removeDefaultExclude('**/.gitattributes') 36 | 37 | def root = "api" 38 | 39 | def propFile = file('local.properties') 40 | def props = new Properties() 41 | 42 | if (propFile.canRead()) { 43 | props.load(new FileInputStream(propFile)) 44 | 45 | if (props != null) { 46 | if (props["api.useLocal"].equals("true")) { 47 | root = props["api.dir"] 48 | } 49 | } 50 | } 51 | 52 | include ':aidl' 53 | project(':aidl').projectDir = file("$root${File.separator}aidl") 54 | 55 | include ':rish' 56 | project(':rish').projectDir = file("$root${File.separator}rish") 57 | 58 | include ':shared' 59 | project(':shared').projectDir = file("$root${File.separator}shared") 60 | 61 | include ':api' 62 | project(':api').projectDir = file("$root${File.separator}api") 63 | 64 | include ':provider' 65 | project(':provider').projectDir = file("$root${File.separator}provider") 66 | 67 | include ':server-shared' 68 | project(':server-shared').projectDir = file("$root${File.separator}server-shared") 69 | -------------------------------------------------------------------------------- /template/magisk_module/.gitattributes: -------------------------------------------------------------------------------- 1 | # Declare files that will always have LF line endings on checkout. 2 | META-INF/** text eol=lf 3 | *.prop text eol=lf 4 | *.sh text eol=lf 5 | *.md text eol=lf 6 | sepolicy.rule text eol=lf 7 | 8 | # Denote all files that are truly binary and should not be modified. 9 | lib/** binary -------------------------------------------------------------------------------- /template/magisk_module/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ### v13.5.1 (2023-09-19) 4 | 5 | - Works on Android 14 6 | - Works on ColorOS (OPPO & OnePlus) Android 14 7 | 8 | ### v13.0.1 (2023-02-01) 9 | 10 | - Fix authentication error of `transactRemote` with `IBinder.FLAG_ONEWAY` 11 | - Fix `rish` does not work on Android 8.x 12 | 13 | ### v12.7.1 (2022-10-06) 14 | 15 | - Don't set `java.library.path` in rish (#38) 16 | - Fix `peekUserService` not work after app process restarts 17 | - Fix UserServices are not killed after permission is revoked 18 | - Fix UserServices are not killed after the app is uninstalled 19 | - Wrap hidden methods of `Instrumentation` (#41) 20 | 21 | ### v12.6.3 (2022-06-09) 22 | 23 | * Works on Android 13 Beta 3 24 | 25 | ### v12.6.2 (2022-04-10) 26 | 27 | * Temporary solution for [#35](https://github.com/RikkaApps/Sui/issues/35) 28 | * Add `updateJson`, module now can be upgraded through Magisk app 29 | 30 | ### v12.6.1 (2022-02-23) 31 | 32 | * Works on Android 13 DP1 (Apps use Sui/Shizuku may need changes also) 33 | * Create `/data/adb/sui` on start (Swithing from Riru version to zygisk version will delete this folder, causing config unable to save) 34 | 35 | ### v12.4.0 (2022-01-09) 36 | 37 | - Adapt recent Magisk (Zygisk) changes 38 | - Remove the shortcut (shows when you long-press Settings) on uninstall (The shortcut that has already added to the launcher still needs manual removal) 39 | 40 | ### v12.3.0 41 | 42 | - Zygisk support 43 | 44 | ### v12.2.1 (2021-09-20) 45 | 46 | - Fix "adb install" under "adb root" 47 | - Fix "adb root" support is not enabled for Android 11 48 | 49 | ### v12.2.0 (2021-09-11) 50 | 51 | - Add adb root support (disabled by default, see description at [GitHub release](https://github.com/RikkaApps/Sui/releases)) 52 | - Fix using UserService will crash sui service on some devices 53 | 54 | ### v12.1.4 (2021-08-28) 55 | 56 | - Make sure original extras from newActivity is not deserialized (For example, this will fix the crash of MIUI Settings "All specs" page) 57 | 58 | ### v12.1.3 (2021-08-26) 59 | 60 | - Fix not working on Sony devices (Not sure if only China version ROMs have this problem) 61 | 62 | ### v12.1.2 (2021-08-26) 63 | 64 | - Bug fix 65 | 66 | ### v12.1.1 (2021-08-25) 67 | 68 | - Don't compile with R8 full mode 69 | 70 | ### v12.1.0 (2021-08-25) 71 | 72 | - [Shizuku API 12](https://github.com/RikkaApps/Shizuku-API/releases/tag/12) 73 | - Add interactive shell support (`post-install.sh` needs changes, see new `post-install.example.sh` for more) 74 | - (Android 8.0+) Long press system settings from the home app, you will find the shortcut of Sui 75 | - (Android 8.0+) Enter "Developer options" in system settings, the system will ask you to add the shortcut of Sui 76 | - Use "the standard Android way" to create UI, the file size increases but a lot more things can achieve 77 | 78 | ### v11.5.0 (2021-03-02) 79 | 80 | - Provide an experimental tool that can run commands, this tool can be used in terminal apps and adb shell (see README for more) 81 | - Filter out packages without components 82 | - Filter out nonexistent packages added by `MATCH_UNINSTALLED_PACKAGES` flag 83 | 84 | > What's the meaning of the command-line tool? There is already "su" from Magisk. 85 | > 86 | > This does helped me to investigate [a bug of Magisk](https://github.com/topjohnwu/Magisk/issues/3976) that happens rarely. At that time, Magisk's su is not available. 87 | 88 | ### v11.4.5 (2021-02-21) 89 | 90 | - Fix random authorization dialog or management ui not showing 91 | 92 | ### v11.4.4 (2021-02-19) 93 | 94 | - Fix developer options crash on Android 12 95 | - Works on devices that have dropped 32-bit support (Android 12 emulator or devices in the future) 96 | 97 | ### v11.4.3 (2021-02-14) 98 | 99 | - Fix installation on x86 100 | 101 | ### v11.4.2 (2021-02-11) 102 | 103 | - Reduce the file size 104 | 105 | ### v11.4.1 (2021-02-07) 106 | 107 | - Fix an undefined behavior 108 | - Skip packages with no code 109 | 110 | ### v11.4 (2021-01-26) 111 | 112 | - Avoid a possible race condition 113 | 114 | ### v11.3 (2021-01-19) 115 | 116 | - Open management UI trough a notification, this notification will show when you are in "Developer options" 117 | 118 | ### v11.2 (2021-01-18) 119 | 120 | - Management UI works more like an `Activity` rather than a window 121 | 122 | ### v11.1 (2021-01-16) 123 | 124 | - Fix permission for multi-process applications 125 | 126 | ### v11.0 (2021-01-16) 127 | 128 | - First public version 129 | -------------------------------------------------------------------------------- /template/magisk_module/META-INF/com/google/android/update-binary: -------------------------------------------------------------------------------- 1 | #!/sbin/sh 2 | 3 | ################# 4 | # Initialization 5 | ################# 6 | 7 | umask 022 8 | 9 | # echo before loading util_functions 10 | ui_print() { echo "$1"; } 11 | 12 | require_new_magisk() { 13 | ui_print "*******************************" 14 | ui_print " Please install Magisk v20.4+! " 15 | ui_print "*******************************" 16 | exit 1 17 | } 18 | 19 | ######################### 20 | # Load util_functions.sh 21 | ######################### 22 | 23 | OUTFD=$2 24 | ZIPFILE=$3 25 | 26 | mount /data 2>/dev/null 27 | 28 | [ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk 29 | . /data/adb/magisk/util_functions.sh 30 | [ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk 31 | 32 | install_module 33 | exit 0 34 | -------------------------------------------------------------------------------- /template/magisk_module/META-INF/com/google/android/updater-script: -------------------------------------------------------------------------------- 1 | #MAGISK 2 | -------------------------------------------------------------------------------- /template/magisk_module/module.prop: -------------------------------------------------------------------------------- 1 | id=${id} 2 | name=${name} 3 | version=${version} 4 | versionCode=${versionCode} 5 | author=${author} 6 | description=${description} 7 | updateJson=${updateJson} 8 | -------------------------------------------------------------------------------- /template/magisk_module/post-fs-data.sh: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | MODDIR=${0%/*} 3 | MODULE_ID=$(basename "$MODDIR") 4 | FLAVOR=@FLAVOR@ 5 | 6 | if [ "$ZYGISK_ENABLED" = false ] && [ "$FLAVOR" = "zygisk" ]; then 7 | log -p w -t "Sui" "Zygisk is disabled, skip zygisk-flavor script" 8 | exit 1 9 | fi 10 | 11 | if [ "$ZYGISK_ENABLED" = true ] && [ "$FLAVOR" = "riru" ]; then 12 | log -p w -t "Sui" "Zygisk is enabled, skip riru-flavor script" 13 | exit 1 14 | fi 15 | 16 | MAGISK_VER_CODE=$(magisk -V) 17 | if [ "$MAGISK_VER_CODE" -ge 21000 ]; then 18 | MAGISK_PATH="$(magisk --path)/.magisk/modules/$MODULE_ID" 19 | else 20 | MAGISK_PATH=/sbin/.magisk/modules/$MODULE_ID 21 | fi 22 | 23 | log -p i -t "Sui" "Magisk version $MAGISK_VER_CODE" 24 | log -p i -t "Sui" "Magisk module path $MAGISK_PATH" 25 | 26 | enable_once="/data/adb/sui/enable_adb_root_once" 27 | enable_forever="/data/adb/sui/enable_adb_root" 28 | adb_root_exit=0 29 | 30 | if [ -f $enable_once ]; then 31 | log -p i -t "Sui" "adb root support is enabled for this time of boot" 32 | rm $enable_once 33 | enable_adb_root=true 34 | fi 35 | 36 | if [ -f $enable_forever ]; then 37 | log -p i -t "Sui" "adb root support is enabled forever" 38 | enable_adb_root=true 39 | fi 40 | 41 | if [ "$enable_adb_root" = true ]; then 42 | log -p i -t "Sui" "Setup adb root support" 43 | 44 | # Run magiskpolicy manually if Magisk does not load sepolicy.rule 45 | if [ ! -e "$(magisk --path)/.magisk/mirror/sepolicy.rules/$MODULE_ID/sepolicy.rule" ]; then 46 | log -p e -t "Sui" "Magisk does not load sepolicy.rule..." 47 | log -p e -t "Sui" "Exec magiskpolicy --live --apply $MAGISK_PATH/sepolicy.rule..." 48 | magiskpolicy --live --apply "$MAGISK_PATH"/sepolicy.rule 49 | log -p i -t "Sui" "Apply finished" 50 | else 51 | log -p i -t "Sui" "Magisk should have loaded sepolicy.rule correctly" 52 | fi 53 | 54 | # Setup adb root support 55 | rm "$MODDIR/bin/adb_root" 56 | ln -s "$MODDIR/bin/sui" "$MODDIR/bin/adb_root" 57 | chmod 700 "$MODDIR/bin/adb_root" 58 | "$MODDIR/bin/adb_root" "$MAGISK_PATH" 59 | adb_root_exit=$? 60 | log -p i -t "Sui" "Exited with $adb_root_exit" 61 | else 62 | log -p i -t "Sui" "adb root support is disabled" 63 | fi 64 | 65 | # Setup uninstaller 66 | rm "$MODDIR/bin/uninstall" 67 | ln -s "$MODDIR/bin/sui" "$MODDIR/bin/uninstall" 68 | 69 | # Run Sui server 70 | chmod 700 "$MODDIR"/bin/sui 71 | exec "$MODDIR"/bin/sui "$MODDIR" "$adb_root_exit" 72 | -------------------------------------------------------------------------------- /template/magisk_module/post-install.example.sh: -------------------------------------------------------------------------------- 1 | # This script will be loaded during the installation process of Sui. 2 | # 3 | # It's designed to automatically copy rish. 4 | # rish is an Android program for interacting with a shell that runs on a high-privileged daemon process (Sui server). 5 | # 6 | # Rename this file to /data/adb/sui/post-install.sh to allow this script to be loaded. 7 | 8 | # This line is required. 9 | SCRIPT_VERSION=2 10 | 11 | # Variables: 12 | # $RISH_DEX: the path to the dex of rish 13 | # $RISH_LIB: the path to the native library of rish 14 | # $RISH_SCRIPT: the path to the wrapper script to start rish 15 | 16 | copy_rish() { 17 | APP_APPLICATION_ID=$1 18 | APP_UID=$2 19 | DEX_TARGET=$3/$4.dex 20 | LIB_TARGET=$3/librish.so 21 | SCRIPT_TARGET=$3/$4 22 | 23 | cp "$RISH_DEX" "$DEX_TARGET" 24 | cp "$RISH_LIB" "$LIB_TARGET" 25 | cp "$RISH_SCRIPT" "$SCRIPT_TARGET" 26 | chmod 600 "$DEX_TARGET" "$LIB_TARGET" 27 | chmod 700 "$SCRIPT_TARGET" 28 | chown "$APP_UID":"$APP_UID" "$DEX_TARGET" "$LIB_TARGET" "$SCRIPT_TARGET" 29 | sed -i "s/%%%RISH_APPLICATION_ID%%%/$APP_APPLICATION_ID/g" "$SCRIPT_TARGET" 30 | } 31 | 32 | # Example: copy rish to /data/local/tmp (for adb) 33 | # 34 | #ui_print "- Copy rish to /data/local/tmp" 35 | #copy_rish com.android.shell 2000 /data/local/tmp rish # Since every app can check if a specific file exists in /data/local/tmp, it's recommended to change the last parameter "rish" to something else 36 | 37 | # Example: copy rish to Termux 38 | # 39 | #ui_print "- Copy rish to Termux" 40 | #APP_UID=$(stat -c '%u' /data/user/0/com.termux) 41 | #copy_rish com.termux $APP_UID /data/user/0/com.termux/files/home rish 42 | -------------------------------------------------------------------------------- /template/magisk_module/riru.sh: -------------------------------------------------------------------------------- 1 | #!/sbin/sh 2 | RIRU_MODULE_LIB_NAME="@RIRU_MODULE_LIB_NAME@" 3 | 4 | # Variables for customize.sh 5 | RIRU_API=0 6 | RIRU_MIN_COMPATIBLE_API=0 7 | RIRU_VERSION_CODE=0 8 | RIRU_VERSION_NAME="" 9 | 10 | # Used by util_functions.sh 11 | RIRU_MODULE_API_VERSION=@RIRU_MODULE_API_VERSION@ 12 | RIRU_MODULE_MIN_API_VERSION=@RIRU_MODULE_MIN_API_VERSION@ 13 | RIRU_MODULE_MIN_RIRU_VERSION_NAME="@RIRU_MODULE_MIN_RIRU_VERSION_NAME@" 14 | 15 | if [ "$MAGISK_VER_CODE" -ge 21000 ]; then 16 | MAGISK_CURRENT_RIRU_MODULE_PATH=$(magisk --path)/.magisk/modules/riru-core 17 | else 18 | MAGISK_CURRENT_RIRU_MODULE_PATH=/sbin/.magisk/modules/riru-core 19 | fi 20 | 21 | if [ ! -d $MAGISK_CURRENT_RIRU_MODULE_PATH ]; then 22 | ui_print "*********************************************************" 23 | ui_print "! Riru is not installed" 24 | ui_print "! Please install Riru from Magisk Manager or https://github.com/RikkaApps/Riru/releases" 25 | abort "*********************************************************" 26 | fi 27 | 28 | if [ -f "$MAGISK_CURRENT_RIRU_MODULE_PATH/disable" ] || [ -f "$MAGISK_CURRENT_RIRU_MODULE_PATH/remove" ]; then 29 | ui_print "*********************************************************" 30 | ui_print "! Riru is not enabled or will be removed" 31 | ui_print "! Please enable Riru in Magisk first" 32 | abort "*********************************************************" 33 | fi 34 | 35 | if [ -f $MAGISK_CURRENT_RIRU_MODULE_PATH/util_functions.sh ]; then 36 | ui_print "- Load $MAGISK_CURRENT_RIRU_MODULE_PATH/util_functions.sh" 37 | # shellcheck disable=SC1090 38 | . $MAGISK_CURRENT_RIRU_MODULE_PATH/util_functions.sh 39 | else 40 | ui_print "*********************************************************" 41 | ui_print "! Riru $RIRU_MODULE_MIN_RIRU_VERSION_NAME or above is required" 42 | ui_print "! Please upgrade Riru from Magisk Manager or https://github.com/RikkaApps/Riru/releases" 43 | abort "*********************************************************" 44 | fi 45 | -------------------------------------------------------------------------------- /template/magisk_module/rish: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | BASEDIR=$(dirname "$0") 3 | DEX="$BASEDIR"/rish.dex 4 | 5 | if [ ! -f "$DEX" ]; then 6 | echo "Cannot find $DEX, please check post-install.sh" 7 | exit 1 8 | fi 9 | 10 | [ -z "$RISH_APPLICATION_ID" ] && export RISH_APPLICATION_ID="%%%RISH_APPLICATION_ID%%%" 11 | /system/bin/app_process -Djava.class.path="$DEX" -Dsui.library.path="$BASEDIR" /system/bin --nice-name=rish rikka.sui.shell.Shell "$@" 12 | -------------------------------------------------------------------------------- /template/magisk_module/sepolicy.rule: -------------------------------------------------------------------------------- 1 | # Allow adbd to transition to magisk:s0 context 2 | allow adbd adbd process setcurrent 3 | allow adbd magisk process dyntransition 4 | 5 | # Fix "adb install" under "adb root" 6 | allow system_server magisk unix_stream_socket { getopt getattr read write } 7 | -------------------------------------------------------------------------------- /template/magisk_module/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/sbin/sh 2 | MODDIR=${0%/*} 3 | MODULES=$(dirname "$MODDIR") 4 | 5 | uninstall() { 6 | chmod 700 "$MODDIR"/bin/uninstall 7 | "$MODDIR"/bin/uninstall "$MODDIR" 8 | rm -rf "/data/adb/sui" 9 | } 10 | 11 | if [ -d "$MODULES/riru_sui" ] && [ -d "$MODULES/zygisk_sui" ]; then 12 | if [ -f "$MODULES/riru_sui/remove" ] && [ -f "$MODULES/zygisk_sui/remove" ]; then 13 | uninstall 14 | fi 15 | else 16 | uninstall 17 | fi 18 | -------------------------------------------------------------------------------- /template/magisk_module/util_functions.sh: -------------------------------------------------------------------------------- 1 | if [ "$ARCH" = "arm64" ]; then 2 | ARCH_NAME="arm64-v8a" 3 | ARCH_NAME_SECONDARY="armeabi-v7a" 4 | ARCH_DIR="lib64" 5 | ARCH_DIR_SECONDARY="lib" 6 | elif [ "$ARCH" = "arm" ]; then 7 | ARCH_NAME="armeabi-v7a" 8 | ARCH_DIR="lib" 9 | elif [ "$ARCH" = "x64" ]; then 10 | ARCH_NAME="x86_64" 11 | ARCH_NAME_SECONDARY="x86" 12 | ARCH_DIR="lib64" 13 | ARCH_DIR_SECONDARY="lib" 14 | elif [ "$ARCH" = "x86" ]; then 15 | ARCH_NAME="x86" 16 | ARCH_DIR="lib" 17 | fi 18 | 19 | enforce_install_from_magisk_app() { 20 | if [ ! "$BOOTMODE" ]; then 21 | ui_print "*********************************************************" 22 | ui_print "! Install from recovery is NOT supported" 23 | ui_print "! Some recovery has broken implementations, install with such recovery will finally cause Riru or Riru modules not working" 24 | ui_print "! Please install from Magisk app" 25 | abort "*********************************************************" 26 | fi 27 | } 28 | 29 | check_arch() { 30 | if [ -z $ARCH_NAME ]; then 31 | abort "! Unsupported platform: $ARCH" 32 | else 33 | ui_print "- Device platform: $ARCH" 34 | fi 35 | } 36 | 37 | check_android_version() { 38 | if [ "$API" -ge 23 ]; then 39 | ui_print "- Android SDK version: $API" 40 | else 41 | ui_print "*********************************************************" 42 | ui_print "! Requires Android 6.0 (API 23) or above" 43 | abort "*********************************************************" 44 | fi 45 | } 46 | 47 | check_magisk_version() { 48 | ui_print "- Magisk version: $MAGISK_VER ($MAGISK_VER_CODE)" 49 | 50 | if [ "$FLAVOR" == "riru" ]; then 51 | ui_print "- Installing Sui (Riru version)" 52 | elif [ "$FLAVOR" == "zygisk" ]; then 53 | ui_print "- Installing Sui (Zygisk version)" 54 | 55 | if [ "$MAGISK_VER_CODE" -lt 23016 ]; then 56 | ui_print "*********************************************************" 57 | ui_print "! Zygisk requires Magisk 23016+" 58 | abort "*********************************************************" 59 | fi 60 | else 61 | ui_print "*********************************************************" 62 | ui_print "! Unsupported flavor $FLAVOR" 63 | abort "*********************************************************" 64 | fi 65 | } 66 | -------------------------------------------------------------------------------- /template/magisk_module/verify.sh: -------------------------------------------------------------------------------- 1 | TMPDIR_FOR_VERIFY="$TMPDIR/.vunzip" 2 | mkdir "$TMPDIR_FOR_VERIFY" 3 | 4 | abort_verify() { 5 | ui_print "*********************************************************" 6 | ui_print "! $1" 7 | ui_print "! This zip may be corrupted, please try downloading again" 8 | abort "*********************************************************" 9 | } 10 | 11 | # extract 12 | extract() { 13 | zip=$1 14 | file=$2 15 | dir=$3 16 | junk_paths=$4 17 | [ -z "$junk_paths" ] && junk_paths=false 18 | opts="-o" 19 | [ $junk_paths = true ] && opts="-oj" 20 | 21 | file_path="" 22 | hash_path="" 23 | if [ $junk_paths = true ]; then 24 | file_path="$dir/$(basename "$file")" 25 | hash_path="$TMPDIR_FOR_VERIFY/$(basename "$file").sha256sum" 26 | else 27 | file_path="$dir/$file" 28 | hash_path="$TMPDIR_FOR_VERIFY/$file.sha256sum" 29 | fi 30 | 31 | unzip $opts "$zip" "$file" -d "$dir" >&2 32 | [ -f "$file_path" ] || abort_verify "$file not exists" 33 | 34 | unzip $opts "$zip" "$file.sha256sum" -d "$TMPDIR_FOR_VERIFY" >&2 35 | [ -f "$hash_path" ] || abort_verify "$file.sha256sum not exists" 36 | 37 | (echo "$(cat "$hash_path") $file_path" | sha256sum -c -s -) || abort_verify "Failed to verify $file" 38 | ui_print "- Verified $file" >&1 39 | } 40 | -------------------------------------------------------------------------------- /ui/.gitignore: -------------------------------------------------------------------------------- 1 | /.externalNativeBuild 2 | /build 3 | /release -------------------------------------------------------------------------------- /ui/aapt2-resources.cfg: -------------------------------------------------------------------------------- 1 | drawable/ic_shortcut_24#no_obfuscate 2 | string/shortcut_is_out_dated#no_obfuscate 3 | -------------------------------------------------------------------------------- /ui/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -repackageclasses rikka.sui 2 | 3 | -keepclasseswithmembers class rikka.sui.SuiActivity { 4 | public (...); 5 | } 6 | 7 | -keepclasseswithmembers class rikka.sui.SuiRequestPermissionDialog { 8 | public (...); 9 | } 10 | 11 | -keepnames class * implements android.os.Parcelable 12 | 13 | -keepclassmembers class * implements android.os.Parcelable { 14 | public static final android.os.Parcelable$Creator CREATOR; 15 | } 16 | 17 | -assumenosideeffects class android.util.Log { 18 | public static *** d(...); 19 | } 20 | 21 | -assumenosideeffects class rikka.sui.util.Logger { 22 | public *** d(...); 23 | } 24 | 25 | -assumenosideeffects class kotlin.jvm.internal.Intrinsics { 26 | public static void check*(...); 27 | public static void throw*(...); 28 | } 29 | 30 | -keepattributes SourceFile,LineNumberTable 31 | -renamesourcefileattribute SourceFile 32 | 33 | -dontwarn android.** 34 | -dontwarn com.android.** 35 | -------------------------------------------------------------------------------- /ui/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/SuiActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui; 21 | 22 | import android.app.ActivityManager; 23 | import android.app.Application; 24 | import android.content.res.Resources; 25 | import android.os.Bundle; 26 | 27 | import androidx.annotation.Nullable; 28 | 29 | import java.util.Objects; 30 | 31 | import rikka.sui.app.AppActivity; 32 | import rikka.sui.management.ManagementFragment; 33 | 34 | public class SuiActivity extends AppActivity { 35 | 36 | public SuiActivity(Application application, Resources resources) { 37 | super(application, resources); 38 | } 39 | 40 | @Override 41 | protected void onCreate(@Nullable Bundle savedInstanceState) { 42 | super.onCreate(savedInstanceState); 43 | setContentView(R.layout.main); 44 | setTitle("Sui"); 45 | Objects.requireNonNull(getSupportActionBar()).setSubtitle(BuildConfig.VERSION_NAME); 46 | 47 | getSupportFragmentManager().beginTransaction() 48 | .replace(R.id.fragment_container, new ManagementFragment()) 49 | .commit(); 50 | 51 | setTaskDescription(new ActivityManager.TaskDescription("Sui")); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/SuiRequestPermissionDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui; 21 | 22 | import android.app.Application; 23 | import android.content.res.Resources; 24 | 25 | import rikka.sui.permission.ConfirmationDialog; 26 | 27 | public class SuiRequestPermissionDialog extends ConfirmationDialog { 28 | 29 | public SuiRequestPermissionDialog( 30 | Application application, Resources resources, 31 | int requestUid, int requestPid, String requestPackageName, int requestCode) { 32 | super(application, resources); 33 | show(requestUid, requestPid, requestPackageName, requestCode); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/app/AppFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.app; 21 | 22 | import androidx.fragment.app.Fragment; 23 | 24 | public class AppFragment extends Fragment { 25 | 26 | public AppActivity getAppActivity() { 27 | return (AppActivity) getActivity(); 28 | } 29 | 30 | public AppActivity requireAppActivity() { 31 | return (AppActivity) requireActivity(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/ktx/Drawable.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package rikka.sui.ktx 18 | 19 | import android.graphics.Bitmap 20 | import android.graphics.Bitmap.Config 21 | import android.graphics.Canvas 22 | import android.graphics.drawable.BitmapDrawable 23 | import android.graphics.drawable.Drawable 24 | import androidx.annotation.Px 25 | 26 | /** 27 | * Return a [Bitmap] representation of this [Drawable]. 28 | * 29 | * If this instance is a [BitmapDrawable] and the [width], [height], and [config] match, the 30 | * underlying [Bitmap] instance will be returned directly. If any of those three properties differ 31 | * then a new [Bitmap] is created. For all other [Drawable] types, a new [Bitmap] is created. 32 | * 33 | * @param width Width of the desired bitmap. Defaults to [Drawable.getIntrinsicWidth]. 34 | * @param height Height of the desired bitmap. Defaults to [Drawable.getIntrinsicHeight]. 35 | * @param config Bitmap config of the desired bitmap. Null attempts to use the native config, if 36 | * any. Defaults to [Config.ARGB_8888] otherwise. 37 | */ 38 | fun Drawable.toBitmap( 39 | @Px width: Int = intrinsicWidth, 40 | @Px height: Int = intrinsicHeight, 41 | config: Config? = null 42 | ): Bitmap { 43 | if (this is BitmapDrawable) { 44 | if (config == null || bitmap.config == config) { 45 | // Fast-path to return original. Bitmap.createScaledBitmap will do this check, but it 46 | // involves allocation and two jumps into native code so we perform the check ourselves. 47 | if (width == intrinsicWidth && height == intrinsicHeight) { 48 | return bitmap 49 | } 50 | return Bitmap.createScaledBitmap(bitmap, width, height, true) 51 | } 52 | } 53 | val oldLeft = bounds.left 54 | val oldTop = bounds.top 55 | val oldRight = bounds.right 56 | val oldBottom = bounds.bottom 57 | 58 | val bitmap = Bitmap.createBitmap(width, height, config ?: Config.ARGB_8888) 59 | setBounds(0, 0, width, height) 60 | draw(Canvas(bitmap)) 61 | 62 | setBounds(oldLeft, oldTop, oldRight, oldBottom) 63 | return bitmap 64 | } 65 | 66 | /** 67 | * Updates this drawable's bounds. This version of the method allows using named parameters 68 | * to just set one or more axes. 69 | * 70 | * @see Drawable.setBounds 71 | */ 72 | fun Drawable.updateBounds( 73 | @Px left: Int = bounds.left, 74 | @Px top: Int = bounds.top, 75 | @Px right: Int = bounds.right, 76 | @Px bottom: Int = bounds.bottom 77 | ) { 78 | setBounds(left, top, right, bottom) 79 | } -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/ktx/Handler.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.ktx 21 | 22 | import android.os.Handler 23 | import android.os.Looper 24 | 25 | val mainHandler by lazy { 26 | Handler(Looper.getMainLooper()) 27 | } -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/ktx/LayoutInflater.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.ktx 21 | 22 | import android.annotation.SuppressLint 23 | import android.content.Context 24 | import android.util.Log 25 | import android.view.LayoutInflater 26 | 27 | @SuppressLint("PrivateApi") 28 | private val constructor = try { 29 | Class.forName("com.android.internal.policy.PhoneLayoutInflater").getDeclaredConstructor(Context::class.java).apply { 30 | isAccessible = true 31 | } 32 | } catch (e: Throwable) { 33 | Log.e("LayoutInflaterKt", Log.getStackTraceString(e)) 34 | null 35 | } 36 | 37 | fun createLayoutInflater(context: Context): LayoutInflater? { 38 | return try { 39 | constructor?.newInstance(context) as LayoutInflater? 40 | } catch (e: Throwable) { 41 | Log.e("LayoutInflaterKt", Log.getStackTraceString(e)) 42 | null 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/ktx/TextView.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.ktx 21 | 22 | import android.view.View 23 | import android.widget.TextView 24 | import rikka.sui.R 25 | 26 | private const val tag_countdown = 1599296841 27 | 28 | fun TextView.applyCountdown(countdownSecond: Int, message: CharSequence? = null, format: Int = 0) { 29 | val countdownRunnable = object : Runnable { 30 | override fun run() { 31 | val countdown = getTag(tag_countdown) as Int 32 | setTag(tag_countdown, countdown - 1) 33 | if (countdown == 0) { 34 | isEnabled = true 35 | if (message != null) text = message 36 | } else { 37 | isEnabled = false 38 | if (message != null && format != 0) text = context.getString(R.string.brackets_format, message, countdown.toString()) 39 | postDelayed(this, 1000) 40 | } 41 | } 42 | } 43 | 44 | val attached = isAttachedToWindow 45 | 46 | setTag(tag_countdown, countdownSecond) 47 | if (attached) { 48 | countdownRunnable.run() 49 | } 50 | 51 | addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener { 52 | override fun onViewAttachedToWindow(view: View) { 53 | if (!attached) { 54 | countdownRunnable.run() 55 | } 56 | } 57 | 58 | override fun onViewDetachedFromWindow(view: View) { 59 | removeOnAttachStateChangeListener(this) 60 | removeCallbacks(countdownRunnable) 61 | } 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/ktx/Window.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.ktx 21 | 22 | import android.view.Window 23 | import android.view.WindowManager 24 | 25 | private val addSystemFlags = try { 26 | Window::class.java.getDeclaredMethod("addSystemFlags", Int::class.javaPrimitiveType) 27 | } catch (e: Throwable) { 28 | null 29 | } 30 | 31 | private val privateFlagsField = try { 32 | WindowManager.LayoutParams::class.java.getDeclaredField("privateFlags") 33 | } catch (e: Throwable) { 34 | null 35 | } 36 | 37 | val SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS: Int = try { 38 | WindowManager.LayoutParams::class.java.getDeclaredField("SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS").getInt(null) 39 | } catch (e: Throwable) { 40 | 0 41 | } 42 | 43 | fun Window.addSystemFlags(flags: Int) { 44 | if (flags == 0) return 45 | addSystemFlags?.invoke(this, flags) 46 | } 47 | 48 | var WindowManager.LayoutParams.privateFlags: Int 49 | get() = try { 50 | privateFlagsField?.getInt(this) ?: 0 51 | } catch (e: Throwable) { 52 | 0 53 | } 54 | set(value) { 55 | try { 56 | privateFlagsField?.setInt(this, value) 57 | } catch (e: Throwable) { 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/management/ManagementAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | package rikka.sui.management 20 | 21 | import rikka.recyclerview.BaseRecyclerViewAdapter 22 | import rikka.recyclerview.ClassCreatorPool 23 | import rikka.sui.model.AppInfo 24 | 25 | class ManagementAdapter : BaseRecyclerViewAdapter() { 26 | 27 | init { 28 | creatorPool.putRule(AppInfo::class.java, ManagementAppItemViewHolder.CREATOR) 29 | setHasStableIds(true) 30 | } 31 | 32 | override fun getItemId(position: Int): Long { 33 | return getItemAt(position).hashCode().toLong() 34 | } 35 | 36 | override fun onCreateCreatorPool(): ClassCreatorPool { 37 | return ClassCreatorPool() 38 | } 39 | 40 | fun updateData(data: List) { 41 | getItems().clear() 42 | getItems().addAll(data) 43 | notifyDataSetChanged() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/management/ManagementViewModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | package rikka.sui.management 20 | 21 | import android.content.Context 22 | import androidx.lifecycle.MutableLiveData 23 | import androidx.lifecycle.ViewModel 24 | import androidx.lifecycle.viewModelScope 25 | import kotlinx.coroutines.CancellationException 26 | import kotlinx.coroutines.Dispatchers 27 | import kotlinx.coroutines.launch 28 | import rikka.lifecycle.Resource 29 | import rikka.lifecycle.Status 30 | import rikka.sui.model.AppInfo 31 | import rikka.sui.util.AppInfoComparator 32 | import rikka.sui.util.BridgeServiceClient 33 | 34 | class ManagementViewModel : ViewModel() { 35 | 36 | private val fullList = ArrayList() 37 | 38 | val appList = MutableLiveData>>(null) 39 | 40 | private fun handleList() { 41 | val list = fullList.sortedWith(AppInfoComparator()).toList() 42 | 43 | appList.postValue(Resource.success(list)) 44 | } 45 | 46 | fun invalidateList() { 47 | if (appList.value?.status != Status.SUCCESS) { 48 | return 49 | } 50 | 51 | viewModelScope.launch(Dispatchers.IO) { 52 | handleList() 53 | } 54 | } 55 | 56 | fun reload(context: Context) { 57 | appList.postValue(Resource.loading(null)) 58 | 59 | viewModelScope.launch(Dispatchers.IO) { 60 | try { 61 | val pm = context.packageManager 62 | val result = BridgeServiceClient.getApplications(-1 /* ALL */).apply { 63 | forEach { it.label = it.packageInfo.applicationInfo.loadLabel(pm) } 64 | } 65 | 66 | fullList.clear() 67 | fullList.addAll(result) 68 | 69 | handleList() 70 | } catch (e: CancellationException) { 71 | 72 | } catch (e: Throwable) { 73 | appList.postValue(Resource.error(e, null)) 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/model/AppInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.model; 21 | 22 | import android.content.pm.PackageInfo; 23 | import android.os.Parcel; 24 | import android.os.Parcelable; 25 | import android.util.Log; 26 | 27 | public class AppInfo implements Parcelable { 28 | 29 | public PackageInfo packageInfo; 30 | public int flags; 31 | public CharSequence label = null; 32 | 33 | public AppInfo() { 34 | } 35 | 36 | protected AppInfo(Parcel in) { 37 | packageInfo = in.readParcelable(PackageInfo.class.getClassLoader()); 38 | flags = in.readInt(); 39 | } 40 | 41 | public static final Creator CREATOR = new Creator() { 42 | @Override 43 | public AppInfo createFromParcel(Parcel in) { 44 | return new AppInfo(in); 45 | } 46 | 47 | @Override 48 | public AppInfo[] newArray(int size) { 49 | return new AppInfo[size]; 50 | } 51 | }; 52 | 53 | @Override 54 | public int describeContents() { 55 | return 0; 56 | } 57 | 58 | @Override 59 | public void writeToParcel(Parcel dest, int flags) { 60 | dest.writeParcelable(packageInfo, flags); 61 | dest.writeInt(this.flags); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/permission/SystemDialogRootView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.permission; 21 | 22 | import android.content.BroadcastReceiver; 23 | import android.content.Context; 24 | import android.content.Intent; 25 | import android.content.IntentFilter; 26 | import android.view.KeyEvent; 27 | import android.view.WindowManager; 28 | import android.widget.FrameLayout; 29 | 30 | import androidx.annotation.NonNull; 31 | 32 | import rikka.sui.util.Logger; 33 | 34 | public class SystemDialogRootView extends FrameLayout { 35 | 36 | private static final Logger LOGGER = new Logger("SystemDialogRootView"); 37 | 38 | private final BroadcastReceiver receiver = new BroadcastReceiver() { 39 | @Override 40 | public void onReceive(Context context, Intent intent) { 41 | onClose(); 42 | dismiss(); 43 | } 44 | }; 45 | private final WindowManager windowManager; 46 | 47 | public SystemDialogRootView(@NonNull Context context) { 48 | super(context); 49 | 50 | windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 51 | } 52 | 53 | public void onClose() { 54 | 55 | } 56 | 57 | public boolean onBackPressed() { 58 | return true; 59 | } 60 | 61 | public final void show(WindowManager.LayoutParams lp) { 62 | try { 63 | windowManager.addView(this, lp); 64 | requestFocus(); 65 | } catch (Exception e) { 66 | LOGGER.w(e, "addView"); 67 | } 68 | } 69 | 70 | public final void dismiss() { 71 | try { 72 | windowManager.removeView(this); 73 | } catch (Throwable e) { 74 | LOGGER.w(e, "removeView"); 75 | } 76 | } 77 | 78 | @Override 79 | public boolean dispatchKeyEvent(KeyEvent event) { 80 | if (event.getKeyCode() != KeyEvent.KEYCODE_BACK) { 81 | return super.dispatchKeyEvent(event); 82 | } 83 | 84 | if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { 85 | getKeyDispatcherState().startTracking(event, this); 86 | return true; 87 | } else if (event.getAction() == KeyEvent.ACTION_UP) { 88 | getKeyDispatcherState().handleUpEvent(event); 89 | 90 | if (event.isTracking() && !event.isCanceled()) { 91 | if (onBackPressed()) { 92 | dismiss(); 93 | } 94 | return true; 95 | } 96 | } 97 | return super.dispatchKeyEvent(event); 98 | } 99 | 100 | @Override 101 | protected void onAttachedToWindow() { 102 | super.onAttachedToWindow(); 103 | 104 | IntentFilter intentFilter = new IntentFilter(); 105 | intentFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 106 | 107 | try { 108 | try { 109 | getContext().registerReceiver(receiver, intentFilter); 110 | LOGGER.i("registerReceiver android.intent.action.CLOSE_SYSTEM_DIALOGS"); 111 | } catch (Exception e) { 112 | LOGGER.w(e, "registerReceiver android.intent.action.CLOSE_SYSTEM_DIALOGS"); 113 | } 114 | } catch (Throwable e) { 115 | LOGGER.w(e, "registerReceiver"); 116 | } 117 | } 118 | 119 | @Override 120 | protected void onDetachedFromWindow() { 121 | super.onDetachedFromWindow(); 122 | 123 | try { 124 | getContext().unregisterReceiver(receiver); 125 | } catch (Throwable e) { 126 | LOGGER.w(e, "unregisterReceiver"); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/server/SuiConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.server; 21 | 22 | public class SuiConfig { 23 | 24 | public static final int FLAG_ALLOWED = 1 << 1; 25 | public static final int FLAG_DENIED = 1 << 2; 26 | public static final int FLAG_HIDDEN = 1 << 3; 27 | public static final int MASK_PERMISSION = FLAG_ALLOWED | FLAG_DENIED | FLAG_HIDDEN; 28 | } 29 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/util/AppIconUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.util; 21 | 22 | import android.content.Context; 23 | import android.graphics.drawable.AdaptiveIconDrawable; 24 | import android.os.Build; 25 | 26 | public class AppIconUtil { 27 | 28 | private static Boolean shouldShrinkNonAdaptiveIcons = null; 29 | 30 | public static boolean shouldShrinkNonAdaptiveIcons(Context context) { 31 | if (shouldShrinkNonAdaptiveIcons == null) { 32 | try { 33 | shouldShrinkNonAdaptiveIcons = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O 34 | && context.getApplicationContext().getPackageManager().getApplicationIcon(context.getPackageName()) instanceof AdaptiveIconDrawable; 35 | } catch (Throwable e) { 36 | shouldShrinkNonAdaptiveIcons = false; 37 | } 38 | } 39 | return shouldShrinkNonAdaptiveIcons; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/util/AppInfoComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.util; 21 | 22 | import java.util.Comparator; 23 | 24 | import rikka.sui.model.AppInfo; 25 | import rikka.sui.util.AppNameComparator; 26 | import rikka.sui.util.UserHandleCompat; 27 | 28 | public class AppInfoComparator implements Comparator { 29 | 30 | private final AppNameComparator appNameComparator = new AppNameComparator<>(new AppInfoProvider()); 31 | 32 | @Override 33 | public int compare(AppInfo o1, AppInfo o2) { 34 | int o1f = o1.flags, o2f = o2.flags; 35 | if (o1.flags == 0) o1f = Integer.MAX_VALUE; 36 | if (o2.flags == 0) o2f = Integer.MAX_VALUE; 37 | int c = Integer.compare(o1f, o2f); 38 | if (c == 0) return appNameComparator.compare(o1, o2); 39 | return c; 40 | } 41 | 42 | private static class AppInfoProvider implements AppNameComparator.InfoProvider { 43 | 44 | @Override 45 | public CharSequence getTitle(AppInfo item) { 46 | if (item.label != null) return item.label; 47 | return item.packageInfo.packageName; 48 | } 49 | 50 | @Override 51 | public String getPackageName(AppInfo item) { 52 | return item.packageInfo.packageName; 53 | } 54 | 55 | @Override 56 | public int getUserId(AppInfo item) { 57 | return UserHandleCompat.getUserId(item.packageInfo.applicationInfo.uid); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/util/AppLabel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package rikka.sui.util; 18 | 19 | import android.content.Context; 20 | import android.content.pm.ApplicationInfo; 21 | import android.text.BidiFormatter; 22 | 23 | import androidx.annotation.NonNull; 24 | 25 | public class AppLabel { 26 | 27 | /** 28 | * The maximum length of a safe label, in characters 29 | */ 30 | public static final int MAX_SAFE_LABEL_LENGTH = 1000; 31 | 32 | public static final float DEFAULT_MAX_LABEL_SIZE_PX = 500f; 33 | 34 | /** 35 | * Get the label for an application, truncating if it is too long. 36 | * 37 | * @param applicationInfo the {@link ApplicationInfo} of the application 38 | * @param context the {@code Context} to retrieve {@code PackageManager} 39 | * @return the label for the application 40 | */ 41 | @NonNull 42 | public static String getAppLabel(@NonNull ApplicationInfo applicationInfo, 43 | @NonNull Context context) { 44 | return getAppLabel(applicationInfo, DEFAULT_MAX_LABEL_SIZE_PX, context); 45 | } 46 | 47 | /** 48 | * Get the label for an application with the ability to control truncating. 49 | * 50 | * @param applicationInfo the {@link ApplicationInfo} of the application 51 | * @param ellipsizeDip see {@link TextUtilsCompat#makeSafeForPresentation}. 52 | * @param context the {@code Context} to retrieve {@code PackageManager} 53 | * @return the label for the application 54 | */ 55 | @NonNull 56 | private static String getAppLabel(@NonNull ApplicationInfo applicationInfo, float ellipsizeDip, 57 | @NonNull Context context) { 58 | String unsafeLabel = applicationInfo.loadLabel(context.getPackageManager()).toString(); 59 | 60 | return BidiFormatter.getInstance().unicodeWrap(TextUtilsCompat.makeSafeForPresentation( 61 | unsafeLabel, MAX_SAFE_LABEL_LENGTH, ellipsizeDip, 62 | TextUtilsCompat.SAFE_STRING_FLAG_TRIM | TextUtilsCompat.SAFE_STRING_FLAG_FIRST_LINE) 63 | .toString()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/util/AppNameComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.util; 21 | 22 | import android.os.Process; 23 | 24 | import java.util.Comparator; 25 | 26 | public class AppNameComparator implements Comparator { 27 | 28 | private final LabelComparator mLabelComparator; 29 | private final InfoProvider mInfoProvider; 30 | 31 | public interface InfoProvider { 32 | CharSequence getTitle(T item); 33 | String getPackageName(T item); 34 | int getUserId(T item); 35 | } 36 | 37 | public AppNameComparator(InfoProvider infoProvider) { 38 | mInfoProvider = infoProvider; 39 | mLabelComparator = new LabelComparator(); 40 | } 41 | 42 | @Override 43 | public int compare(T a, T b) { 44 | // Order by the title in the current locale 45 | int result = mLabelComparator.compare( 46 | mInfoProvider.getTitle(a).toString(), 47 | mInfoProvider.getTitle(b).toString()); 48 | 49 | if (result != 0) { 50 | return result; 51 | } 52 | 53 | // If labels are same, compare component names 54 | result = mInfoProvider.getPackageName(a).compareTo(mInfoProvider.getPackageName(b)); 55 | if (result != 0) { 56 | return result; 57 | } 58 | 59 | if (Process.myUserHandle().hashCode() == mInfoProvider.getUserId(a)) { 60 | return -1; 61 | } else { 62 | int aUserId = mInfoProvider.getUserId(a); 63 | int bUserId = mInfoProvider.getUserId(b); 64 | return Integer.compare(aUserId, bUserId); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/util/LabelComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package rikka.sui.util; 17 | 18 | import java.text.Collator; 19 | import java.util.Comparator; 20 | 21 | /** 22 | * Extension of {@link java.text.Collator} with special handling for digits. Used for comparing 23 | * user visible labels. 24 | */ 25 | public class LabelComparator implements Comparator { 26 | 27 | private final Collator mCollator = Collator.getInstance(); 28 | 29 | @Override 30 | public int compare(String titleA, String titleB) { 31 | // Ensure that we de-prioritize any titles that don't start with a 32 | // linguistic letter or digit 33 | boolean aStartsWithLetter = (titleA.length() > 0) && 34 | Character.isLetterOrDigit(titleA.codePointAt(0)); 35 | boolean bStartsWithLetter = (titleB.length() > 0) && 36 | Character.isLetterOrDigit(titleB.codePointAt(0)); 37 | if (aStartsWithLetter && !bStartsWithLetter) { 38 | return -1; 39 | } else if (!aStartsWithLetter && bStartsWithLetter) { 40 | return 1; 41 | } 42 | 43 | // Order by the title in the current locale 44 | return mCollator.compare(titleA, titleB); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/util/Unsafe.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.util; 21 | 22 | public class Unsafe { 23 | @SuppressWarnings("unchecked") 24 | public static T unsafeCast(Object object) { 25 | return (T) object; 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /ui/src/main/java/rikka/sui/util/UserHandleCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sui. 3 | * 4 | * Sui is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * Sui is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with Sui. If not, see . 16 | * 17 | * Copyright (c) 2021 Sui Contributors 18 | */ 19 | 20 | package rikka.sui.util; 21 | 22 | public class UserHandleCompat { 23 | 24 | private static final int UID = android.system.Os.getuid(); 25 | public static final int PER_USER_RANGE = 100000; 26 | 27 | public static int getUserId(int uid) { 28 | return uid / PER_USER_RANGE; 29 | } 30 | 31 | public static int getAppId(int uid) { 32 | return uid % PER_USER_RANGE; 33 | } 34 | 35 | public static int myUserId() { 36 | return getUserId(UID); 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /ui/src/main/res/anim/stagger_layout_animation.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 13 | 16 | 17 | 18 | 19 | 24 | 25 | -------------------------------------------------------------------------------- /ui/src/main/res/animator/alpha_animator.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 15 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ui/src/main/res/color/confirmation_dialog_button_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ui/src/main/res/drawable/confirmation_dialog_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ui/src/main/res/drawable/ic_close_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /ui/src/main/res/drawable/ic_shortcut_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /ui/src/main/res/drawable/ic_su_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /ui/src/main/res/layout/appbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 27 | 28 | -------------------------------------------------------------------------------- /ui/src/main/res/layout/appbar_fragment_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ui/src/main/res/layout/confirmation_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 26 | 27 | 37 | 38 | 43 | 44 | 55 | 56 | 61 | 62 | 73 | 74 | 79 | 80 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /ui/src/main/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 19 | 20 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ui/src/main/res/layout/management.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 13 | 14 | 19 | 20 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /ui/src/main/res/layout/management_app_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 23 | 24 | 33 | 34 | 43 | 44 | 53 | 54 | 55 | 56 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /ui/src/main/res/raw/keep.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /ui/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |