Compare commits
2 commits
main
...
v4.0.0-sta
| Author | SHA1 | Date | |
|---|---|---|---|
| 938198bf11 | |||
| b7554a5383 |
124 changed files with 2896 additions and 5507 deletions
674
LICENSE
674
LICENSE
|
|
@ -1,674 +0,0 @@
|
||||||
GNU GENERAL PUBLIC LICENSE
|
|
||||||
Version 3, 29 June 2007
|
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
Preamble
|
|
||||||
|
|
||||||
The GNU General Public License is a free, copyleft license for
|
|
||||||
software and other kinds of works.
|
|
||||||
|
|
||||||
The licenses for most software and other practical works are designed
|
|
||||||
to take away your freedom to share and change the works. By contrast,
|
|
||||||
the GNU General Public License is intended to guarantee your freedom to
|
|
||||||
share and change all versions of a program--to make sure it remains free
|
|
||||||
software for all its users. We, the Free Software Foundation, use the
|
|
||||||
GNU General Public License for most of our software; it applies also to
|
|
||||||
any other work released this way by its authors. You can apply it to
|
|
||||||
your programs, too.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom, not
|
|
||||||
price. Our General Public Licenses are designed to make sure that you
|
|
||||||
have the freedom to distribute copies of free software (and charge for
|
|
||||||
them if you wish), that you receive source code or can get it if you
|
|
||||||
want it, that you can change the software or use pieces of it in new
|
|
||||||
free programs, and that you know you can do these things.
|
|
||||||
|
|
||||||
To protect your rights, we need to prevent others from denying you
|
|
||||||
these rights or asking you to surrender the rights. Therefore, you have
|
|
||||||
certain responsibilities if you distribute copies of the software, or if
|
|
||||||
you modify it: responsibilities to respect the freedom of others.
|
|
||||||
|
|
||||||
For example, if you distribute copies of such a program, whether
|
|
||||||
gratis or for a fee, you must pass on to the recipients the same
|
|
||||||
freedoms that you received. You must make sure that they, too, receive
|
|
||||||
or can get the source code. And you must show them these terms so they
|
|
||||||
know their rights.
|
|
||||||
|
|
||||||
Developers that use the GNU GPL protect your rights with two steps:
|
|
||||||
(1) assert copyright on the software, and (2) offer you this License
|
|
||||||
giving you legal permission to copy, distribute and/or modify it.
|
|
||||||
|
|
||||||
For the developers' and authors' protection, the GPL clearly explains
|
|
||||||
that there is no warranty for this free software. For both users' and
|
|
||||||
authors' sake, the GPL requires that modified versions be marked as
|
|
||||||
changed, so that their problems will not be attributed erroneously to
|
|
||||||
authors of previous versions.
|
|
||||||
|
|
||||||
Some devices are designed to deny users access to install or run
|
|
||||||
modified versions of the software inside them, although the manufacturer
|
|
||||||
can do so. This is fundamentally incompatible with the aim of
|
|
||||||
protecting users' freedom to change the software. The systematic
|
|
||||||
pattern of such abuse occurs in the area of products for individuals to
|
|
||||||
use, which is precisely where it is most unacceptable. Therefore, we
|
|
||||||
have designed this version of the GPL to prohibit the practice for those
|
|
||||||
products. If such problems arise substantially in other domains, we
|
|
||||||
stand ready to extend this provision to those domains in future versions
|
|
||||||
of the GPL, as needed to protect the freedom of users.
|
|
||||||
|
|
||||||
Finally, every program is threatened constantly by software patents.
|
|
||||||
States should not allow patents to restrict development and use of
|
|
||||||
software on general-purpose computers, but in those that do, we wish to
|
|
||||||
avoid the special danger that patents applied to a free program could
|
|
||||||
make it effectively proprietary. To prevent this, the GPL assures that
|
|
||||||
patents cannot be used to render the program non-free.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
|
||||||
modification follow.
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
0. Definitions.
|
|
||||||
|
|
||||||
"This License" refers to version 3 of the GNU General Public License.
|
|
||||||
|
|
||||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
|
||||||
works, such as semiconductor masks.
|
|
||||||
|
|
||||||
"The Program" refers to any copyrightable work licensed under this
|
|
||||||
License. Each licensee is addressed as "you". "Licensees" and
|
|
||||||
"recipients" may be individuals or organizations.
|
|
||||||
|
|
||||||
To "modify" a work means to copy from or adapt all or part of the work
|
|
||||||
in a fashion requiring copyright permission, other than the making of an
|
|
||||||
exact copy. The resulting work is called a "modified version" of the
|
|
||||||
earlier work or a work "based on" the earlier work.
|
|
||||||
|
|
||||||
A "covered work" means either the unmodified Program or a work based
|
|
||||||
on the Program.
|
|
||||||
|
|
||||||
To "propagate" a work means to do anything with it that, without
|
|
||||||
permission, would make you directly or secondarily liable for
|
|
||||||
infringement under applicable copyright law, except executing it on a
|
|
||||||
computer or modifying a private copy. Propagation includes copying,
|
|
||||||
distribution (with or without modification), making available to the
|
|
||||||
public, and in some countries other activities as well.
|
|
||||||
|
|
||||||
To "convey" a work means any kind of propagation that enables other
|
|
||||||
parties to make or receive copies. Mere interaction with a user through
|
|
||||||
a computer network, with no transfer of a copy, is not conveying.
|
|
||||||
|
|
||||||
An interactive user interface displays "Appropriate Legal Notices"
|
|
||||||
to the extent that it includes a convenient and prominently visible
|
|
||||||
feature that (1) displays an appropriate copyright notice, and (2)
|
|
||||||
tells the user that there is no warranty for the work (except to the
|
|
||||||
extent that warranties are provided), that licensees may convey the
|
|
||||||
work under this License, and how to view a copy of this License. If
|
|
||||||
the interface presents a list of user commands or options, such as a
|
|
||||||
menu, a prominent item in the list meets this criterion.
|
|
||||||
|
|
||||||
1. Source Code.
|
|
||||||
|
|
||||||
The "source code" for a work means the preferred form of the work
|
|
||||||
for making modifications to it. "Object code" means any non-source
|
|
||||||
form of a work.
|
|
||||||
|
|
||||||
A "Standard Interface" means an interface that either is an official
|
|
||||||
standard defined by a recognized standards body, or, in the case of
|
|
||||||
interfaces specified for a particular programming language, one that
|
|
||||||
is widely used among developers working in that language.
|
|
||||||
|
|
||||||
The "System Libraries" of an executable work include anything, other
|
|
||||||
than the work as a whole, that (a) is included in the normal form of
|
|
||||||
packaging a Major Component, but which is not part of that Major
|
|
||||||
Component, and (b) serves only to enable use of the work with that
|
|
||||||
Major Component, or to implement a Standard Interface for which an
|
|
||||||
implementation is available to the public in source code form. A
|
|
||||||
"Major Component", in this context, means a major essential component
|
|
||||||
(kernel, window system, and so on) of the specific operating system
|
|
||||||
(if any) on which the executable work runs, or a compiler used to
|
|
||||||
produce the work, or an object code interpreter used to run it.
|
|
||||||
|
|
||||||
The "Corresponding Source" for a work in object code form means all
|
|
||||||
the source code needed to generate, install, and (for an executable
|
|
||||||
work) run the object code and to modify the work, including scripts to
|
|
||||||
control those activities. However, it does not include the work's
|
|
||||||
System Libraries, or general-purpose tools or generally available free
|
|
||||||
programs which are used unmodified in performing those activities but
|
|
||||||
which are not part of the work. For example, Corresponding Source
|
|
||||||
includes interface definition files associated with source files for
|
|
||||||
the work, and the source code for shared libraries and dynamically
|
|
||||||
linked subprograms that the work is specifically designed to require,
|
|
||||||
such as by intimate data communication or control flow between those
|
|
||||||
subprograms and other parts of the work.
|
|
||||||
|
|
||||||
The Corresponding Source need not include anything that users
|
|
||||||
can regenerate automatically from other parts of the Corresponding
|
|
||||||
Source.
|
|
||||||
|
|
||||||
The Corresponding Source for a work in source code form is that
|
|
||||||
same work.
|
|
||||||
|
|
||||||
2. Basic Permissions.
|
|
||||||
|
|
||||||
All rights granted under this License are granted for the term of
|
|
||||||
copyright on the Program, and are irrevocable provided the stated
|
|
||||||
conditions are met. This License explicitly affirms your unlimited
|
|
||||||
permission to run the unmodified Program. The output from running a
|
|
||||||
covered work is covered by this License only if the output, given its
|
|
||||||
content, constitutes a covered work. This License acknowledges your
|
|
||||||
rights of fair use or other equivalent, as provided by copyright law.
|
|
||||||
|
|
||||||
You may make, run and propagate covered works that you do not
|
|
||||||
convey, without conditions so long as your license otherwise remains
|
|
||||||
in force. You may convey covered works to others for the sole purpose
|
|
||||||
of having them make modifications exclusively for you, or provide you
|
|
||||||
with facilities for running those works, provided that you comply with
|
|
||||||
the terms of this License in conveying all material for which you do
|
|
||||||
not control copyright. Those thus making or running the covered works
|
|
||||||
for you must do so exclusively on your behalf, under your direction
|
|
||||||
and control, on terms that prohibit them from making any copies of
|
|
||||||
your copyrighted material outside their relationship with you.
|
|
||||||
|
|
||||||
Conveying under any other circumstances is permitted solely under
|
|
||||||
the conditions stated below. Sublicensing is not allowed; section 10
|
|
||||||
makes it unnecessary.
|
|
||||||
|
|
||||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
|
||||||
|
|
||||||
No covered work shall be deemed part of an effective technological
|
|
||||||
measure under any applicable law fulfilling obligations under article
|
|
||||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
|
||||||
similar laws prohibiting or restricting circumvention of such
|
|
||||||
measures.
|
|
||||||
|
|
||||||
When you convey a covered work, you waive any legal power to forbid
|
|
||||||
circumvention of technological measures to the extent such circumvention
|
|
||||||
is effected by exercising rights under this License with respect to
|
|
||||||
the covered work, and you disclaim any intention to limit operation or
|
|
||||||
modification of the work as a means of enforcing, against the work's
|
|
||||||
users, your or third parties' legal rights to forbid circumvention of
|
|
||||||
technological measures.
|
|
||||||
|
|
||||||
4. Conveying Verbatim Copies.
|
|
||||||
|
|
||||||
You may convey verbatim copies of the Program's source code as you
|
|
||||||
receive it, in any medium, provided that you conspicuously and
|
|
||||||
appropriately publish on each copy an appropriate copyright notice;
|
|
||||||
keep intact all notices stating that this License and any
|
|
||||||
non-permissive terms added in accord with section 7 apply to the code;
|
|
||||||
keep intact all notices of the absence of any warranty; and give all
|
|
||||||
recipients a copy of this License along with the Program.
|
|
||||||
|
|
||||||
You may charge any price or no price for each copy that you convey,
|
|
||||||
and you may offer support or warranty protection for a fee.
|
|
||||||
|
|
||||||
5. Conveying Modified Source Versions.
|
|
||||||
|
|
||||||
You may convey a work based on the Program, or the modifications to
|
|
||||||
produce it from the Program, in the form of source code under the
|
|
||||||
terms of section 4, provided that you also meet all of these conditions:
|
|
||||||
|
|
||||||
a) The work must carry prominent notices stating that you modified
|
|
||||||
it, and giving a relevant date.
|
|
||||||
|
|
||||||
b) The work must carry prominent notices stating that it is
|
|
||||||
released under this License and any conditions added under section
|
|
||||||
7. This requirement modifies the requirement in section 4 to
|
|
||||||
"keep intact all notices".
|
|
||||||
|
|
||||||
c) You must license the entire work, as a whole, under this
|
|
||||||
License to anyone who comes into possession of a copy. This
|
|
||||||
License will therefore apply, along with any applicable section 7
|
|
||||||
additional terms, to the whole of the work, and all its parts,
|
|
||||||
regardless of how they are packaged. This License gives no
|
|
||||||
permission to license the work in any other way, but it does not
|
|
||||||
invalidate such permission if you have separately received it.
|
|
||||||
|
|
||||||
d) If the work has interactive user interfaces, each must display
|
|
||||||
Appropriate Legal Notices; however, if the Program has interactive
|
|
||||||
interfaces that do not display Appropriate Legal Notices, your
|
|
||||||
work need not make them do so.
|
|
||||||
|
|
||||||
A compilation of a covered work with other separate and independent
|
|
||||||
works, which are not by their nature extensions of the covered work,
|
|
||||||
and which are not combined with it such as to form a larger program,
|
|
||||||
in or on a volume of a storage or distribution medium, is called an
|
|
||||||
"aggregate" if the compilation and its resulting copyright are not
|
|
||||||
used to limit the access or legal rights of the compilation's users
|
|
||||||
beyond what the individual works permit. Inclusion of a covered work
|
|
||||||
in an aggregate does not cause this License to apply to the other
|
|
||||||
parts of the aggregate.
|
|
||||||
|
|
||||||
6. Conveying Non-Source Forms.
|
|
||||||
|
|
||||||
You may convey a covered work in object code form under the terms
|
|
||||||
of sections 4 and 5, provided that you also convey the
|
|
||||||
machine-readable Corresponding Source under the terms of this License,
|
|
||||||
in one of these ways:
|
|
||||||
|
|
||||||
a) Convey the object code in, or embodied in, a physical product
|
|
||||||
(including a physical distribution medium), accompanied by the
|
|
||||||
Corresponding Source fixed on a durable physical medium
|
|
||||||
customarily used for software interchange.
|
|
||||||
|
|
||||||
b) Convey the object code in, or embodied in, a physical product
|
|
||||||
(including a physical distribution medium), accompanied by a
|
|
||||||
written offer, valid for at least three years and valid for as
|
|
||||||
long as you offer spare parts or customer support for that product
|
|
||||||
model, to give anyone who possesses the object code either (1) a
|
|
||||||
copy of the Corresponding Source for all the software in the
|
|
||||||
product that is covered by this License, on a durable physical
|
|
||||||
medium customarily used for software interchange, for a price no
|
|
||||||
more than your reasonable cost of physically performing this
|
|
||||||
conveying of source, or (2) access to copy the
|
|
||||||
Corresponding Source from a network server at no charge.
|
|
||||||
|
|
||||||
c) Convey individual copies of the object code with a copy of the
|
|
||||||
written offer to provide the Corresponding Source. This
|
|
||||||
alternative is allowed only occasionally and noncommercially, and
|
|
||||||
only if you received the object code with such an offer, in accord
|
|
||||||
with subsection 6b.
|
|
||||||
|
|
||||||
d) Convey the object code by offering access from a designated
|
|
||||||
place (gratis or for a charge), and offer equivalent access to the
|
|
||||||
Corresponding Source in the same way through the same place at no
|
|
||||||
further charge. You need not require recipients to copy the
|
|
||||||
Corresponding Source along with the object code. If the place to
|
|
||||||
copy the object code is a network server, the Corresponding Source
|
|
||||||
may be on a different server (operated by you or a third party)
|
|
||||||
that supports equivalent copying facilities, provided you maintain
|
|
||||||
clear directions next to the object code saying where to find the
|
|
||||||
Corresponding Source. Regardless of what server hosts the
|
|
||||||
Corresponding Source, you remain obligated to ensure that it is
|
|
||||||
available for as long as needed to satisfy these requirements.
|
|
||||||
|
|
||||||
e) Convey the object code using peer-to-peer transmission, provided
|
|
||||||
you inform other peers where the object code and Corresponding
|
|
||||||
Source of the work are being offered to the general public at no
|
|
||||||
charge under subsection 6d.
|
|
||||||
|
|
||||||
A separable portion of the object code, whose source code is excluded
|
|
||||||
from the Corresponding Source as a System Library, need not be
|
|
||||||
included in conveying the object code work.
|
|
||||||
|
|
||||||
A "User Product" is either (1) a "consumer product", which means any
|
|
||||||
tangible personal property which is normally used for personal, family,
|
|
||||||
or household purposes, or (2) anything designed or sold for incorporation
|
|
||||||
into a dwelling. In determining whether a product is a consumer product,
|
|
||||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
|
||||||
product received by a particular user, "normally used" refers to a
|
|
||||||
typical or common use of that class of product, regardless of the status
|
|
||||||
of the particular user or of the way in which the particular user
|
|
||||||
actually uses, or expects or is expected to use, the product. A product
|
|
||||||
is a consumer product regardless of whether the product has substantial
|
|
||||||
commercial, industrial or non-consumer uses, unless such uses represent
|
|
||||||
the only significant mode of use of the product.
|
|
||||||
|
|
||||||
"Installation Information" for a User Product means any methods,
|
|
||||||
procedures, authorization keys, or other information required to install
|
|
||||||
and execute modified versions of a covered work in that User Product from
|
|
||||||
a modified version of its Corresponding Source. The information must
|
|
||||||
suffice to ensure that the continued functioning of the modified object
|
|
||||||
code is in no case prevented or interfered with solely because
|
|
||||||
modification has been made.
|
|
||||||
|
|
||||||
If you convey an object code work under this section in, or with, or
|
|
||||||
specifically for use in, a User Product, and the conveying occurs as
|
|
||||||
part of a transaction in which the right of possession and use of the
|
|
||||||
User Product is transferred to the recipient in perpetuity or for a
|
|
||||||
fixed term (regardless of how the transaction is characterized), the
|
|
||||||
Corresponding Source conveyed under this section must be accompanied
|
|
||||||
by the Installation Information. But this requirement does not apply
|
|
||||||
if neither you nor any third party retains the ability to install
|
|
||||||
modified object code on the User Product (for example, the work has
|
|
||||||
been installed in ROM).
|
|
||||||
|
|
||||||
The requirement to provide Installation Information does not include a
|
|
||||||
requirement to continue to provide support service, warranty, or updates
|
|
||||||
for a work that has been modified or installed by the recipient, or for
|
|
||||||
the User Product in which it has been modified or installed. Access to a
|
|
||||||
network may be denied when the modification itself materially and
|
|
||||||
adversely affects the operation of the network or violates the rules and
|
|
||||||
protocols for communication across the network.
|
|
||||||
|
|
||||||
Corresponding Source conveyed, and Installation Information provided,
|
|
||||||
in accord with this section must be in a format that is publicly
|
|
||||||
documented (and with an implementation available to the public in
|
|
||||||
source code form), and must require no special password or key for
|
|
||||||
unpacking, reading or copying.
|
|
||||||
|
|
||||||
7. Additional Terms.
|
|
||||||
|
|
||||||
"Additional permissions" are terms that supplement the terms of this
|
|
||||||
License by making exceptions from one or more of its conditions.
|
|
||||||
Additional permissions that are applicable to the entire Program shall
|
|
||||||
be treated as though they were included in this License, to the extent
|
|
||||||
that they are valid under applicable law. If additional permissions
|
|
||||||
apply only to part of the Program, that part may be used separately
|
|
||||||
under those permissions, but the entire Program remains governed by
|
|
||||||
this License without regard to the additional permissions.
|
|
||||||
|
|
||||||
When you convey a copy of a covered work, you may at your option
|
|
||||||
remove any additional permissions from that copy, or from any part of
|
|
||||||
it. (Additional permissions may be written to require their own
|
|
||||||
removal in certain cases when you modify the work.) You may place
|
|
||||||
additional permissions on material, added by you to a covered work,
|
|
||||||
for which you have or can give appropriate copyright permission.
|
|
||||||
|
|
||||||
Notwithstanding any other provision of this License, for material you
|
|
||||||
add to a covered work, you may (if authorized by the copyright holders of
|
|
||||||
that material) supplement the terms of this License with terms:
|
|
||||||
|
|
||||||
a) Disclaiming warranty or limiting liability differently from the
|
|
||||||
terms of sections 15 and 16 of this License; or
|
|
||||||
|
|
||||||
b) Requiring preservation of specified reasonable legal notices or
|
|
||||||
author attributions in that material or in the Appropriate Legal
|
|
||||||
Notices displayed by works containing it; or
|
|
||||||
|
|
||||||
c) Prohibiting misrepresentation of the origin of that material, or
|
|
||||||
requiring that modified versions of such material be marked in
|
|
||||||
reasonable ways as different from the original version; or
|
|
||||||
|
|
||||||
d) Limiting the use for publicity purposes of names of licensors or
|
|
||||||
authors of the material; or
|
|
||||||
|
|
||||||
e) Declining to grant rights under trademark law for use of some
|
|
||||||
trade names, trademarks, or service marks; or
|
|
||||||
|
|
||||||
f) Requiring indemnification of licensors and authors of that
|
|
||||||
material by anyone who conveys the material (or modified versions of
|
|
||||||
it) with contractual assumptions of liability to the recipient, for
|
|
||||||
any liability that these contractual assumptions directly impose on
|
|
||||||
those licensors and authors.
|
|
||||||
|
|
||||||
All other non-permissive additional terms are considered "further
|
|
||||||
restrictions" within the meaning of section 10. If the Program as you
|
|
||||||
received it, or any part of it, contains a notice stating that it is
|
|
||||||
governed by this License along with a term that is a further
|
|
||||||
restriction, you may remove that term. If a license document contains
|
|
||||||
a further restriction but permits relicensing or conveying under this
|
|
||||||
License, you may add to a covered work material governed by the terms
|
|
||||||
of that license document, provided that the further restriction does
|
|
||||||
not survive such relicensing or conveying.
|
|
||||||
|
|
||||||
If you add terms to a covered work in accord with this section, you
|
|
||||||
must place, in the relevant source files, a statement of the
|
|
||||||
additional terms that apply to those files, or a notice indicating
|
|
||||||
where to find the applicable terms.
|
|
||||||
|
|
||||||
Additional terms, permissive or non-permissive, may be stated in the
|
|
||||||
form of a separately written license, or stated as exceptions;
|
|
||||||
the above requirements apply either way.
|
|
||||||
|
|
||||||
8. Termination.
|
|
||||||
|
|
||||||
You may not propagate or modify a covered work except as expressly
|
|
||||||
provided under this License. Any attempt otherwise to propagate or
|
|
||||||
modify it is void, and will automatically terminate your rights under
|
|
||||||
this License (including any patent licenses granted under the third
|
|
||||||
paragraph of section 11).
|
|
||||||
|
|
||||||
However, if you cease all violation of this License, then your
|
|
||||||
license from a particular copyright holder is reinstated (a)
|
|
||||||
provisionally, unless and until the copyright holder explicitly and
|
|
||||||
finally terminates your license, and (b) permanently, if the copyright
|
|
||||||
holder fails to notify you of the violation by some reasonable means
|
|
||||||
prior to 60 days after the cessation.
|
|
||||||
|
|
||||||
Moreover, your license from a particular copyright holder is
|
|
||||||
reinstated permanently if the copyright holder notifies you of the
|
|
||||||
violation by some reasonable means, this is the first time you have
|
|
||||||
received notice of violation of this License (for any work) from that
|
|
||||||
copyright holder, and you cure the violation prior to 30 days after
|
|
||||||
your receipt of the notice.
|
|
||||||
|
|
||||||
Termination of your rights under this section does not terminate the
|
|
||||||
licenses of parties who have received copies or rights from you under
|
|
||||||
this License. If your rights have been terminated and not permanently
|
|
||||||
reinstated, you do not qualify to receive new licenses for the same
|
|
||||||
material under section 10.
|
|
||||||
|
|
||||||
9. Acceptance Not Required for Having Copies.
|
|
||||||
|
|
||||||
You are not required to accept this License in order to receive or
|
|
||||||
run a copy of the Program. Ancillary propagation of a covered work
|
|
||||||
occurring solely as a consequence of using peer-to-peer transmission
|
|
||||||
to receive a copy likewise does not require acceptance. However,
|
|
||||||
nothing other than this License grants you permission to propagate or
|
|
||||||
modify any covered work. These actions infringe copyright if you do
|
|
||||||
not accept this License. Therefore, by modifying or propagating a
|
|
||||||
covered work, you indicate your acceptance of this License to do so.
|
|
||||||
|
|
||||||
10. Automatic Licensing of Downstream Recipients.
|
|
||||||
|
|
||||||
Each time you convey a covered work, the recipient automatically
|
|
||||||
receives a license from the original licensors, to run, modify and
|
|
||||||
propagate that work, subject to this License. You are not responsible
|
|
||||||
for enforcing compliance by third parties with this License.
|
|
||||||
|
|
||||||
An "entity transaction" is a transaction transferring control of an
|
|
||||||
organization, or substantially all assets of one, or subdividing an
|
|
||||||
organization, or merging organizations. If propagation of a covered
|
|
||||||
work results from an entity transaction, each party to that
|
|
||||||
transaction who receives a copy of the work also receives whatever
|
|
||||||
licenses to the work the party's predecessor in interest had or could
|
|
||||||
give under the previous paragraph, plus a right to possession of the
|
|
||||||
Corresponding Source of the work from the predecessor in interest, if
|
|
||||||
the predecessor has it or can get it with reasonable efforts.
|
|
||||||
|
|
||||||
You may not impose any further restrictions on the exercise of the
|
|
||||||
rights granted or affirmed under this License. For example, you may
|
|
||||||
not impose a license fee, royalty, or other charge for exercise of
|
|
||||||
rights granted under this License, and you may not initiate litigation
|
|
||||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
|
||||||
any patent claim is infringed by making, using, selling, offering for
|
|
||||||
sale, or importing the Program or any portion of it.
|
|
||||||
|
|
||||||
11. Patents.
|
|
||||||
|
|
||||||
A "contributor" is a copyright holder who authorizes use under this
|
|
||||||
License of the Program or a work on which the Program is based. The
|
|
||||||
work thus licensed is called the contributor's "contributor version".
|
|
||||||
|
|
||||||
A contributor's "essential patent claims" are all patent claims
|
|
||||||
owned or controlled by the contributor, whether already acquired or
|
|
||||||
hereafter acquired, that would be infringed by some manner, permitted
|
|
||||||
by this License, of making, using, or selling its contributor version,
|
|
||||||
but do not include claims that would be infringed only as a
|
|
||||||
consequence of further modification of the contributor version. For
|
|
||||||
purposes of this definition, "control" includes the right to grant
|
|
||||||
patent sublicenses in a manner consistent with the requirements of
|
|
||||||
this License.
|
|
||||||
|
|
||||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
|
||||||
patent license under the contributor's essential patent claims, to
|
|
||||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
|
||||||
propagate the contents of its contributor version.
|
|
||||||
|
|
||||||
In the following three paragraphs, a "patent license" is any express
|
|
||||||
agreement or commitment, however denominated, not to enforce a patent
|
|
||||||
(such as an express permission to practice a patent or covenant not to
|
|
||||||
sue for patent infringement). To "grant" such a patent license to a
|
|
||||||
party means to make such an agreement or commitment not to enforce a
|
|
||||||
patent against the party.
|
|
||||||
|
|
||||||
If you convey a covered work, knowingly relying on a patent license,
|
|
||||||
and the Corresponding Source of the work is not available for anyone
|
|
||||||
to copy, free of charge and under the terms of this License, through a
|
|
||||||
publicly available network server or other readily accessible means,
|
|
||||||
then you must either (1) cause the Corresponding Source to be so
|
|
||||||
available, or (2) arrange to deprive yourself of the benefit of the
|
|
||||||
patent license for this particular work, or (3) arrange, in a manner
|
|
||||||
consistent with the requirements of this License, to extend the patent
|
|
||||||
license to downstream recipients. "Knowingly relying" means you have
|
|
||||||
actual knowledge that, but for the patent license, your conveying the
|
|
||||||
covered work in a country, or your recipient's use of the covered work
|
|
||||||
in a country, would infringe one or more identifiable patents in that
|
|
||||||
country that you have reason to believe are valid.
|
|
||||||
|
|
||||||
If, pursuant to or in connection with a single transaction or
|
|
||||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
|
||||||
covered work, and grant a patent license to some of the parties
|
|
||||||
receiving the covered work authorizing them to use, propagate, modify
|
|
||||||
or convey a specific copy of the covered work, then the patent license
|
|
||||||
you grant is automatically extended to all recipients of the covered
|
|
||||||
work and works based on it.
|
|
||||||
|
|
||||||
A patent license is "discriminatory" if it does not include within
|
|
||||||
the scope of its coverage, prohibits the exercise of, or is
|
|
||||||
conditioned on the non-exercise of one or more of the rights that are
|
|
||||||
specifically granted under this License. You may not convey a covered
|
|
||||||
work if you are a party to an arrangement with a third party that is
|
|
||||||
in the business of distributing software, under which you make payment
|
|
||||||
to the third party based on the extent of your activity of conveying
|
|
||||||
the work, and under which the third party grants, to any of the
|
|
||||||
parties who would receive the covered work from you, a discriminatory
|
|
||||||
patent license (a) in connection with copies of the covered work
|
|
||||||
conveyed by you (or copies made from those copies), or (b) primarily
|
|
||||||
for and in connection with specific products or compilations that
|
|
||||||
contain the covered work, unless you entered into that arrangement,
|
|
||||||
or that patent license was granted, prior to 28 March 2007.
|
|
||||||
|
|
||||||
Nothing in this License shall be construed as excluding or limiting
|
|
||||||
any implied license or other defenses to infringement that may
|
|
||||||
otherwise be available to you under applicable patent law.
|
|
||||||
|
|
||||||
12. No Surrender of Others' Freedom.
|
|
||||||
|
|
||||||
If conditions are imposed on you (whether by court order, agreement or
|
|
||||||
otherwise) that contradict the conditions of this License, they do not
|
|
||||||
excuse you from the conditions of this License. If you cannot convey a
|
|
||||||
covered work so as to satisfy simultaneously your obligations under this
|
|
||||||
License and any other pertinent obligations, then as a consequence you may
|
|
||||||
not convey it at all. For example, if you agree to terms that obligate you
|
|
||||||
to collect a royalty for further conveying from those to whom you convey
|
|
||||||
the Program, the only way you could satisfy both those terms and this
|
|
||||||
License would be to refrain entirely from conveying the Program.
|
|
||||||
|
|
||||||
13. Use with the GNU Affero General Public License.
|
|
||||||
|
|
||||||
Notwithstanding any other provision of this License, you have
|
|
||||||
permission to link or combine any covered work with a work licensed
|
|
||||||
under version 3 of the GNU Affero General Public License into a single
|
|
||||||
combined work, and to convey the resulting work. The terms of this
|
|
||||||
License will continue to apply to the part which is the covered work,
|
|
||||||
but the special requirements of the GNU Affero General Public License,
|
|
||||||
section 13, concerning interaction through a network will apply to the
|
|
||||||
combination as such.
|
|
||||||
|
|
||||||
14. Revised Versions of this License.
|
|
||||||
|
|
||||||
The Free Software Foundation may publish revised and/or new versions of
|
|
||||||
the GNU General Public License from time to time. Such new versions will
|
|
||||||
be similar in spirit to the present version, but may differ in detail to
|
|
||||||
address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the
|
|
||||||
Program specifies that a certain numbered version of the GNU General
|
|
||||||
Public License "or any later version" applies to it, you have the
|
|
||||||
option of following the terms and conditions either of that numbered
|
|
||||||
version or of any later version published by the Free Software
|
|
||||||
Foundation. If the Program does not specify a version number of the
|
|
||||||
GNU General Public License, you may choose any version ever published
|
|
||||||
by the Free Software Foundation.
|
|
||||||
|
|
||||||
If the Program specifies that a proxy can decide which future
|
|
||||||
versions of the GNU General Public License can be used, that proxy's
|
|
||||||
public statement of acceptance of a version permanently authorizes you
|
|
||||||
to choose that version for the Program.
|
|
||||||
|
|
||||||
Later license versions may give you additional or different
|
|
||||||
permissions. However, no additional obligations are imposed on any
|
|
||||||
author or copyright holder as a result of your choosing to follow a
|
|
||||||
later version.
|
|
||||||
|
|
||||||
15. Disclaimer of Warranty.
|
|
||||||
|
|
||||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
|
||||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
|
||||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
|
||||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
|
||||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
|
||||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
|
||||||
|
|
||||||
16. Limitation of Liability.
|
|
||||||
|
|
||||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
|
||||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
|
||||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
|
||||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
|
||||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
|
||||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
|
||||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
|
||||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
|
||||||
SUCH DAMAGES.
|
|
||||||
|
|
||||||
17. Interpretation of Sections 15 and 16.
|
|
||||||
|
|
||||||
If the disclaimer of warranty and limitation of liability provided
|
|
||||||
above cannot be given local legal effect according to their terms,
|
|
||||||
reviewing courts shall apply local law that most closely approximates
|
|
||||||
an absolute waiver of all civil liability in connection with the
|
|
||||||
Program, unless a warranty or assumption of liability accompanies a
|
|
||||||
copy of the Program in return for a fee.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
How to Apply These Terms to Your New Programs
|
|
||||||
|
|
||||||
If you develop a new program, and you want it to be of the greatest
|
|
||||||
possible use to the public, the best way to achieve this is to make it
|
|
||||||
free software which everyone can redistribute and change under these terms.
|
|
||||||
|
|
||||||
To do so, attach the following notices to the program. It is safest
|
|
||||||
to attach them to the start of each source file to most effectively
|
|
||||||
state the exclusion of warranty; and each file should have at least
|
|
||||||
the "copyright" line and a pointer to where the full notice is found.
|
|
||||||
|
|
||||||
<one line to give the program's name and a brief idea of what it does.>
|
|
||||||
Copyright (C) <year> <name of author>
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
|
||||||
|
|
||||||
If the program does terminal interaction, make it output a short
|
|
||||||
notice like this when it starts in an interactive mode:
|
|
||||||
|
|
||||||
<program> Copyright (C) <year> <name of author>
|
|
||||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
|
||||||
This is free software, and you are welcome to redistribute it
|
|
||||||
under certain conditions; type `show c' for details.
|
|
||||||
|
|
||||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
|
||||||
parts of the General Public License. Of course, your program's commands
|
|
||||||
might be different; for a GUI interface, you would use an "about box".
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or school,
|
|
||||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
|
||||||
For more information on this, and how to apply and follow the GNU GPL, see
|
|
||||||
<https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The GNU General Public License does not permit incorporating your program
|
|
||||||
into proprietary programs. If your program is a subroutine library, you
|
|
||||||
may consider it more useful to permit linking proprietary applications with
|
|
||||||
the library. If this is what you want to do, use the GNU Lesser General
|
|
||||||
Public License instead of this License. But first, please read
|
|
||||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
# Reporting Security Issues
|
|
||||||
|
|
||||||
The KernelSU team and community take security bugs in KernelSU seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
|
|
||||||
|
|
||||||
To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/tiann/KernelSU/security/advisories/new) tab, or you can mailto [weishu](mailto:twsxtd@gmail.com) directly.
|
|
||||||
|
|
||||||
The KernelSU team will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
project_id_env: CROWDIN_PROJECT_ID
|
|
||||||
api_token_env: CROWDIN_API_TOKEN
|
|
||||||
preserve_hierarchy: 1
|
|
||||||
files:
|
|
||||||
- source: /manager/app/src/main/res/values/strings.xml
|
|
||||||
translation: /manager/app/src/main/res/values-%two_letters_code%/strings.xml
|
|
||||||
14
justfile
14
justfile
|
|
@ -1,14 +0,0 @@
|
||||||
alias bk := build_ksud
|
|
||||||
alias bm := build_manager
|
|
||||||
|
|
||||||
build_ksud:
|
|
||||||
cross build --target aarch64-linux-android --release --manifest-path ./userspace/ksud/Cargo.toml
|
|
||||||
|
|
||||||
build_manager: build_ksud
|
|
||||||
cp userspace/ksud/target/aarch64-linux-android/release/ksud manager/app/src/main/jniLibs/arm64-v8a/libksud.so
|
|
||||||
cd manager && ./gradlew aDebug
|
|
||||||
|
|
||||||
clippy:
|
|
||||||
cargo fmt --manifest-path ./userspace/ksud/Cargo.toml
|
|
||||||
cross clippy --target x86_64-pc-windows-gnu --release --manifest-path ./userspace/ksud/Cargo.toml
|
|
||||||
cross clippy --target aarch64-linux-android --release --manifest-path ./userspace/ksud/Cargo.toml
|
|
||||||
|
|
@ -47,7 +47,7 @@ static void remove_uid_from_arr(uid_t uid)
|
||||||
if (allow_list_pointer == 0)
|
if (allow_list_pointer == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
temp_arr = kzalloc(sizeof(allow_list_arr), GFP_KERNEL);
|
temp_arr = kmalloc(sizeof(allow_list_arr), GFP_KERNEL);
|
||||||
if (temp_arr == NULL) {
|
if (temp_arr == NULL) {
|
||||||
pr_err("%s: unable to allocate memory\n", __func__);
|
pr_err("%s: unable to allocate memory\n", __func__);
|
||||||
return;
|
return;
|
||||||
|
|
@ -200,7 +200,7 @@ bool ksu_set_app_profile(struct app_profile *profile, bool persist)
|
||||||
}
|
}
|
||||||
|
|
||||||
// not found, alloc a new node!
|
// not found, alloc a new node!
|
||||||
p = (struct perm_data *)kzalloc(sizeof(struct perm_data), GFP_KERNEL);
|
p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
pr_err("ksu_set_app_profile alloc failed\n");
|
pr_err("ksu_set_app_profile alloc failed\n");
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ static struct sdesc *init_sdesc(struct crypto_shash *alg)
|
||||||
int size;
|
int size;
|
||||||
|
|
||||||
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
|
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
|
||||||
sdesc = kzalloc(size, GFP_KERNEL);
|
sdesc = kmalloc(size, GFP_KERNEL);
|
||||||
if (!sdesc)
|
if (!sdesc)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
sdesc->shash.tfm = alg;
|
sdesc->shash.tfm = alg;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@
|
||||||
#include "selinux/selinux.h"
|
#include "selinux/selinux.h"
|
||||||
#include "syscall_hook_manager.h"
|
#include "syscall_hook_manager.h"
|
||||||
#include "sucompat.h"
|
#include "sucompat.h"
|
||||||
|
|
||||||
#include "sulog.h"
|
#include "sulog.h"
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION (6, 7, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION (6, 7, 0)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ enum ksu_feature_id {
|
||||||
KSU_FEATURE_SU_COMPAT = 0,
|
KSU_FEATURE_SU_COMPAT = 0,
|
||||||
KSU_FEATURE_KERNEL_UMOUNT = 1,
|
KSU_FEATURE_KERNEL_UMOUNT = 1,
|
||||||
KSU_FEATURE_ENHANCED_SECURITY = 2,
|
KSU_FEATURE_ENHANCED_SECURITY = 2,
|
||||||
KSU_FEATURE_SULOG = 3,
|
|
||||||
|
|
||||||
KSU_FEATURE_MAX
|
KSU_FEATURE_MAX
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,24 @@ static const struct ksu_feature_handler kernel_umount_handler = {
|
||||||
.set_handler = kernel_umount_feature_set,
|
.set_handler = kernel_umount_feature_set,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool should_umount(struct path *path)
|
||||||
|
{
|
||||||
|
if (!path) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) {
|
||||||
|
pr_info("ignore global mnt namespace process: %d\n", current_uid().val);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) {
|
||||||
|
const char *fstype = path->mnt->mnt_sb->s_type->name;
|
||||||
|
return strcmp(fstype, "overlay") == 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
extern int path_umount(struct path *path, int flags);
|
extern int path_umount(struct path *path, int flags);
|
||||||
|
|
||||||
static void ksu_umount_mnt(struct path *path, int flags)
|
static void ksu_umount_mnt(struct path *path, int flags)
|
||||||
|
|
@ -53,7 +71,7 @@ static void ksu_umount_mnt(struct path *path, int flags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void try_umount(const char *mnt, int flags)
|
void try_umount(const char *mnt, bool check_mnt, int flags)
|
||||||
{
|
{
|
||||||
struct path path;
|
struct path path;
|
||||||
int err = kern_path(mnt, 0, &path);
|
int err = kern_path(mnt, 0, &path);
|
||||||
|
|
@ -67,6 +85,12 @@ void try_umount(const char *mnt, int flags)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we are only interest in some specific mounts
|
||||||
|
if (check_mnt && !should_umount(&path)) {
|
||||||
|
path_put(&path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ksu_umount_mnt(&path, flags);
|
ksu_umount_mnt(&path, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,14 +107,8 @@ static void umount_tw_func(struct callback_head *cb)
|
||||||
saved = override_creds(tw->old_cred);
|
saved = override_creds(tw->old_cred);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct mount_entry *entry;
|
// fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
|
||||||
down_read(&mount_list_lock);
|
// filter the mountpoint whose target is `/data/adb`
|
||||||
list_for_each_entry(entry, &mount_list, list) {
|
|
||||||
pr_info("%s: unmounting: %s flags 0x%x\n", __func__, entry->umountable, entry->flags);
|
|
||||||
try_umount(entry->umountable, entry->flags);
|
|
||||||
}
|
|
||||||
up_read(&mount_list_lock);
|
|
||||||
|
|
||||||
ksu_umount_manager_execute_all(tw->old_cred);
|
ksu_umount_manager_execute_all(tw->old_cred);
|
||||||
|
|
||||||
if (saved)
|
if (saved)
|
||||||
|
|
@ -138,7 +156,7 @@ int ksu_handle_umount(uid_t old_uid, uid_t new_uid)
|
||||||
// umount the target mnt
|
// umount the target mnt
|
||||||
pr_info("handle umount for uid: %d, pid: %d\n", new_uid, current->pid);
|
pr_info("handle umount for uid: %d, pid: %d\n", new_uid, current->pid);
|
||||||
|
|
||||||
tw = kzalloc(sizeof(*tw), GFP_ATOMIC);
|
tw = kmalloc(sizeof(*tw), GFP_ATOMIC);
|
||||||
if (!tw)
|
if (!tw)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,24 +2,13 @@
|
||||||
#define __KSU_H_KERNEL_UMOUNT
|
#define __KSU_H_KERNEL_UMOUNT
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/list.h>
|
|
||||||
#include <linux/rwsem.h>
|
|
||||||
|
|
||||||
void ksu_kernel_umount_init(void);
|
void ksu_kernel_umount_init(void);
|
||||||
void ksu_kernel_umount_exit(void);
|
void ksu_kernel_umount_exit(void);
|
||||||
|
|
||||||
void try_umount(const char *mnt, int flags);
|
void try_umount(const char *mnt, bool check_mnt, int flags);
|
||||||
|
|
||||||
// Handler function to be called from setresuid hook
|
// Handler function to be called from setresuid hook
|
||||||
int ksu_handle_umount(uid_t old_uid, uid_t new_uid);
|
int ksu_handle_umount(uid_t old_uid, uid_t new_uid);
|
||||||
|
|
||||||
// for the umount list
|
#endif
|
||||||
struct mount_entry {
|
|
||||||
char *umountable;
|
|
||||||
unsigned int flags;
|
|
||||||
struct list_head list;
|
|
||||||
};
|
|
||||||
extern struct list_head mount_list;
|
|
||||||
extern struct rw_semaphore mount_list_lock;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -92,14 +92,14 @@ void on_post_fs_data(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void ext4_unregister_sysfs(struct super_block *sb);
|
extern void ext4_unregister_sysfs(struct super_block *sb);
|
||||||
int nuke_ext4_sysfs(const char* mnt)
|
static void nuke_ext4_sysfs(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_EXT4_FS
|
#ifdef CONFIG_EXT4_FS
|
||||||
struct path path;
|
struct path path;
|
||||||
int err = kern_path(mnt, 0, &path);
|
int err = kern_path("/data/adb/modules", 0, &path);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_err("nuke path err: %d\n", err);
|
pr_err("nuke path err: %d\n", err);
|
||||||
return err;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct super_block *sb = path.dentry->d_inode->i_sb;
|
struct super_block *sb = path.dentry->d_inode->i_sb;
|
||||||
|
|
@ -107,19 +107,18 @@ int nuke_ext4_sysfs(const char* mnt)
|
||||||
if (strcmp(name, "ext4") != 0) {
|
if (strcmp(name, "ext4") != 0) {
|
||||||
pr_info("nuke but module aren't mounted\n");
|
pr_info("nuke but module aren't mounted\n");
|
||||||
path_put(&path);
|
path_put(&path);
|
||||||
return -EINVAL;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ext4_unregister_sysfs(sb);
|
ext4_unregister_sysfs(sb);
|
||||||
path_put(&path);
|
path_put(&path);
|
||||||
|
|
||||||
return 0;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_module_mounted(void){
|
void on_module_mounted(void){
|
||||||
pr_info("on_module_mounted!\n");
|
pr_info("on_module_mounted!\n");
|
||||||
ksu_module_mounted = true;
|
ksu_module_mounted = true;
|
||||||
|
nuke_ext4_sysfs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_boot_completed(void){
|
void on_boot_completed(void){
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,6 @@ void on_boot_completed(void);
|
||||||
|
|
||||||
bool ksu_is_safe_mode(void);
|
bool ksu_is_safe_mode(void);
|
||||||
|
|
||||||
int nuke_ext4_sysfs(const char* mnt);
|
|
||||||
|
|
||||||
extern u32 ksu_file_sid;
|
extern u32 ksu_file_sid;
|
||||||
extern bool ksu_module_mounted;
|
extern bool ksu_module_mounted;
|
||||||
extern bool ksu_boot_completed;
|
extern bool ksu_boot_completed;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/binfmts.h>
|
#include <linux/binfmts.h>
|
||||||
|
|
||||||
#include "manual_su.h"
|
#include "manual_su.h"
|
||||||
#include "ksu.h"
|
#include "ksu.h"
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
|
|
@ -50,7 +49,7 @@ static char* get_token_from_envp(void)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
env_copy = kzalloc(env_len + 1, GFP_KERNEL);
|
env_copy = kmalloc(env_len + 1, GFP_KERNEL);
|
||||||
if (!env_copy) {
|
if (!env_copy) {
|
||||||
up_read(&mm->mmap_lock);
|
up_read(&mm->mmap_lock);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -73,7 +72,7 @@ static char* get_token_from_envp(void)
|
||||||
char *token_end = strchr(token_start, '\0');
|
char *token_end = strchr(token_start, '\0');
|
||||||
|
|
||||||
if (token_end && (token_end - token_start) == KSU_TOKEN_LENGTH) {
|
if (token_end && (token_end - token_start) == KSU_TOKEN_LENGTH) {
|
||||||
token = kzalloc(KSU_TOKEN_LENGTH + 1, GFP_KERNEL);
|
token = kmalloc(KSU_TOKEN_LENGTH + 1, GFP_KERNEL);
|
||||||
if (token) {
|
if (token) {
|
||||||
memcpy(token, token_start, KSU_TOKEN_LENGTH);
|
memcpy(token, token_start, KSU_TOKEN_LENGTH);
|
||||||
token[KSU_TOKEN_LENGTH] = '\0';
|
token[KSU_TOKEN_LENGTH] = '\0';
|
||||||
|
|
|
||||||
|
|
@ -354,7 +354,7 @@ static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
|
||||||
|
|
||||||
if (datum->u.xperms == NULL) {
|
if (datum->u.xperms == NULL) {
|
||||||
datum->u.xperms =
|
datum->u.xperms =
|
||||||
(struct avtab_extended_perms *)(kzalloc(
|
(struct avtab_extended_perms *)(kmalloc(
|
||||||
sizeof(xperms), GFP_KERNEL));
|
sizeof(xperms), GFP_KERNEL));
|
||||||
if (!datum->u.xperms) {
|
if (!datum->u.xperms) {
|
||||||
pr_err("alloc xperms failed\n");
|
pr_err("alloc xperms failed\n");
|
||||||
|
|
@ -548,7 +548,7 @@ static bool add_filename_trans(struct policydb *db, const char *s,
|
||||||
trans = (struct filename_trans_datum *)kcalloc(1 ,sizeof(*trans),
|
trans = (struct filename_trans_datum *)kcalloc(1 ,sizeof(*trans),
|
||||||
GFP_ATOMIC);
|
GFP_ATOMIC);
|
||||||
struct filename_trans_key *new_key =
|
struct filename_trans_key *new_key =
|
||||||
(struct filename_trans_key *)kzalloc(sizeof(*new_key),
|
(struct filename_trans_key *)kmalloc(sizeof(*new_key),
|
||||||
GFP_ATOMIC);
|
GFP_ATOMIC);
|
||||||
*new_key = key;
|
*new_key = key;
|
||||||
new_key->name = kstrdup(key.name, GFP_ATOMIC);
|
new_key->name = kstrdup(key.name, GFP_ATOMIC);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
#include "app_profile.h"
|
#include "app_profile.h"
|
||||||
#include "syscall_hook_manager.h"
|
#include "syscall_hook_manager.h"
|
||||||
|
|
||||||
|
|
||||||
#include "sulog.h"
|
#include "sulog.h"
|
||||||
|
|
||||||
#define SU_PATH "/system/bin/su"
|
#define SU_PATH "/system/bin/su"
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,8 @@
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
#include "klog.h"
|
#include "klog.h"
|
||||||
|
|
||||||
#include "sulog.h"
|
#include "sulog.h"
|
||||||
#include "ksu.h"
|
#include "ksu.h"
|
||||||
#include "feature.h"
|
|
||||||
|
|
||||||
#if __SULOG_GATE
|
#if __SULOG_GATE
|
||||||
|
|
||||||
|
|
@ -26,28 +24,7 @@ static DEFINE_SPINLOCK(dedup_lock);
|
||||||
static LIST_HEAD(sulog_queue);
|
static LIST_HEAD(sulog_queue);
|
||||||
static struct workqueue_struct *sulog_workqueue;
|
static struct workqueue_struct *sulog_workqueue;
|
||||||
static struct work_struct sulog_work;
|
static struct work_struct sulog_work;
|
||||||
static bool sulog_enabled __read_mostly = true;
|
static bool sulog_enabled = true;
|
||||||
|
|
||||||
static int sulog_feature_get(u64 *value)
|
|
||||||
{
|
|
||||||
*value = sulog_enabled ? 1 : 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sulog_feature_set(u64 value)
|
|
||||||
{
|
|
||||||
bool enable = value != 0;
|
|
||||||
sulog_enabled = enable;
|
|
||||||
pr_info("sulog: set to %d\n", enable);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct ksu_feature_handler sulog_handler = {
|
|
||||||
.feature_id = KSU_FEATURE_SULOG,
|
|
||||||
.name = "sulog",
|
|
||||||
.get_handler = sulog_feature_get,
|
|
||||||
.set_handler = sulog_feature_set,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void get_timestamp(char *buf, size_t len)
|
static void get_timestamp(char *buf, size_t len)
|
||||||
{
|
{
|
||||||
|
|
@ -203,7 +180,7 @@ static void sulog_add_entry(char *log_buf, size_t len, uid_t uid, u8 dedup_type)
|
||||||
if (!dedup_should_print(uid, dedup_type, log_buf, len))
|
if (!dedup_should_print(uid, dedup_type, log_buf, len))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
|
entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
|
||||||
if (!entry)
|
if (!entry)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -326,10 +303,6 @@ void ksu_sulog_report_syscall(uid_t uid, const char *comm, const char *syscall,
|
||||||
|
|
||||||
int ksu_sulog_init(void)
|
int ksu_sulog_init(void)
|
||||||
{
|
{
|
||||||
if (ksu_register_feature_handler(&sulog_handler)) {
|
|
||||||
pr_err("Failed to register sulog feature handler\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
sulog_workqueue = alloc_workqueue("ksu_sulog", WQ_UNBOUND | WQ_HIGHPRI, 1);
|
sulog_workqueue = alloc_workqueue("ksu_sulog", WQ_UNBOUND | WQ_HIGHPRI, 1);
|
||||||
if (!sulog_workqueue) {
|
if (!sulog_workqueue) {
|
||||||
pr_err("sulog: failed to create workqueue\n");
|
pr_err("sulog: failed to create workqueue\n");
|
||||||
|
|
@ -346,8 +319,6 @@ void ksu_sulog_exit(void)
|
||||||
struct sulog_entry *entry, *tmp;
|
struct sulog_entry *entry, *tmp;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
ksu_unregister_feature_handler(KSU_FEATURE_SULOG);
|
|
||||||
|
|
||||||
sulog_enabled = false;
|
sulog_enabled = false;
|
||||||
|
|
||||||
if (sulog_workqueue) {
|
if (sulog_workqueue) {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
extern struct timezone sys_tz;
|
extern struct timezone sys_tz;
|
||||||
|
|
||||||
#define SULOG_PATH "/data/adb/ksu/log/sulog.log"
|
#define SULOG_PATH "/data/adb/ksu/log/sulog.log"
|
||||||
#define SULOG_MAX_SIZE (32 * 1024 * 1024) // 128MB
|
#define SULOG_MAX_SIZE (128 * 1024 * 1024) // 128MB
|
||||||
#define SULOG_ENTRY_MAX_LEN 512
|
#define SULOG_ENTRY_MAX_LEN 512
|
||||||
#define SULOG_COMM_LEN 256
|
#define SULOG_COMM_LEN 256
|
||||||
#define DEDUP_SECS 10
|
#define DEDUP_SECS 10
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include "supercalls.h"
|
||||||
|
|
||||||
#include <linux/anon_inodes.h>
|
#include <linux/anon_inodes.h>
|
||||||
#include <linux/capability.h>
|
#include <linux/capability.h>
|
||||||
#include <linux/cred.h>
|
#include <linux/cred.h>
|
||||||
|
|
@ -12,14 +14,13 @@
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
|
|
||||||
#include "supercalls.h"
|
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "feature.h"
|
#include "feature.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksud.h"
|
#include "ksud.h"
|
||||||
#include "kernel_umount.h"
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
#include "sulog.h"
|
||||||
#include "selinux/selinux.h"
|
#include "selinux/selinux.h"
|
||||||
#include "objsec.h"
|
#include "objsec.h"
|
||||||
#include "file_wrapper.h"
|
#include "file_wrapper.h"
|
||||||
|
|
@ -28,7 +29,6 @@
|
||||||
#include "dynamic_manager.h"
|
#include "dynamic_manager.h"
|
||||||
#include "umount_manager.h"
|
#include "umount_manager.h"
|
||||||
|
|
||||||
#include "sulog.h"
|
|
||||||
#ifdef CONFIG_KSU_MANUAL_SU
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
#include "manual_su.h"
|
#include "manual_su.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -481,141 +481,6 @@ static int do_manage_mark(void __user *arg)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_nuke_ext4_sysfs(void __user *arg)
|
|
||||||
{
|
|
||||||
struct ksu_nuke_ext4_sysfs_cmd cmd;
|
|
||||||
char mnt[256];
|
|
||||||
long ret;
|
|
||||||
|
|
||||||
if (copy_from_user(&cmd, arg, sizeof(cmd)))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
if (!cmd.arg)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
memset(mnt, 0, sizeof(mnt));
|
|
||||||
|
|
||||||
ret = strncpy_from_user(mnt, cmd.arg, sizeof(mnt));
|
|
||||||
if (ret < 0) {
|
|
||||||
pr_err("nuke ext4 copy mnt failed: %ld\\n", ret);
|
|
||||||
return -EFAULT; // 或者 return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret == sizeof(mnt)) {
|
|
||||||
pr_err("nuke ext4 mnt path too long\\n");
|
|
||||||
return -ENAMETOOLONG;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("do_nuke_ext4_sysfs: %s\n", mnt);
|
|
||||||
|
|
||||||
return nuke_ext4_sysfs(mnt);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct list_head mount_list = LIST_HEAD_INIT(mount_list);
|
|
||||||
DECLARE_RWSEM(mount_list_lock);
|
|
||||||
|
|
||||||
static int add_try_umount(void __user *arg)
|
|
||||||
{
|
|
||||||
struct mount_entry *new_entry, *entry, *tmp;
|
|
||||||
struct ksu_add_try_umount_cmd cmd;
|
|
||||||
char buf[256] = {0};
|
|
||||||
|
|
||||||
if (copy_from_user(&cmd, arg, sizeof cmd))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
switch (cmd.mode) {
|
|
||||||
case KSU_UMOUNT_WIPE: {
|
|
||||||
struct mount_entry *entry, *tmp;
|
|
||||||
down_write(&mount_list_lock);
|
|
||||||
list_for_each_entry_safe(entry, tmp, &mount_list, list) {
|
|
||||||
pr_info("wipe_umount_list: removing entry: %s\n", entry->umountable);
|
|
||||||
list_del(&entry->list);
|
|
||||||
kfree(entry->umountable);
|
|
||||||
kfree(entry);
|
|
||||||
}
|
|
||||||
up_write(&mount_list_lock);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
case KSU_UMOUNT_ADD: {
|
|
||||||
long len = strncpy_from_user(buf, (const char __user *)cmd.arg, 256);
|
|
||||||
if (len <= 0)
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
buf[sizeof(buf) - 1] = '\0';
|
|
||||||
|
|
||||||
new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
|
|
||||||
if (!new_entry)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
new_entry->umountable = kstrdup(buf, GFP_KERNEL);
|
|
||||||
if (!new_entry->umountable) {
|
|
||||||
kfree(new_entry);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
down_write(&mount_list_lock);
|
|
||||||
|
|
||||||
// disallow dupes
|
|
||||||
// if this gets too many, we can consider moving this whole task to a kthread
|
|
||||||
list_for_each_entry(entry, &mount_list, list) {
|
|
||||||
if (!strcmp(entry->umountable, buf)) {
|
|
||||||
pr_info("cmd_add_try_umount: %s is already here!\n", buf);
|
|
||||||
up_write(&mount_list_lock);
|
|
||||||
kfree(new_entry->umountable);
|
|
||||||
kfree(new_entry);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now check flags and add
|
|
||||||
// this also serves as a null check
|
|
||||||
if (cmd.flags)
|
|
||||||
new_entry->flags = cmd.flags;
|
|
||||||
else
|
|
||||||
new_entry->flags = 0;
|
|
||||||
|
|
||||||
// debug
|
|
||||||
list_add(&new_entry->list, &mount_list);
|
|
||||||
up_write(&mount_list_lock);
|
|
||||||
pr_info("cmd_add_try_umount: %s added!\n", buf);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is just strcmp'd wipe anyway
|
|
||||||
case KSU_UMOUNT_DEL: {
|
|
||||||
long len = strncpy_from_user(buf, (const char __user *)cmd.arg, sizeof(buf) - 1);
|
|
||||||
if (len <= 0)
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
buf[sizeof(buf) - 1] = '\0';
|
|
||||||
|
|
||||||
down_write(&mount_list_lock);
|
|
||||||
list_for_each_entry_safe(entry, tmp, &mount_list, list) {
|
|
||||||
if (!strcmp(entry->umountable, buf)) {
|
|
||||||
pr_info("cmd_add_try_umount: entry removed: %s\n", entry->umountable);
|
|
||||||
list_del(&entry->list);
|
|
||||||
kfree(entry->umountable);
|
|
||||||
kfree(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
up_write(&mount_list_lock);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
pr_err("cmd_add_try_umount: invalid operation %u\n", cmd.mode);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // switch(cmd.mode)
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 100. GET_FULL_VERSION - Get full version string
|
// 100. GET_FULL_VERSION - Get full version string
|
||||||
static int do_get_full_version(void __user *arg)
|
static int do_get_full_version(void __user *arg)
|
||||||
{
|
{
|
||||||
|
|
@ -827,7 +692,7 @@ static int do_umount_manager(void __user *arg)
|
||||||
|
|
||||||
switch (cmd.operation) {
|
switch (cmd.operation) {
|
||||||
case UMOUNT_OP_ADD: {
|
case UMOUNT_OP_ADD: {
|
||||||
return ksu_umount_manager_add(cmd.path, cmd.flags, false);
|
return ksu_umount_manager_add(cmd.path, cmd.check_mnt, cmd.flags, false);
|
||||||
}
|
}
|
||||||
case UMOUNT_OP_REMOVE: {
|
case UMOUNT_OP_REMOVE: {
|
||||||
return ksu_umount_manager_remove(cmd.path);
|
return ksu_umount_manager_remove(cmd.path);
|
||||||
|
|
@ -863,8 +728,6 @@ static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
|
||||||
{ .cmd = KSU_IOCTL_SET_FEATURE, .name = "SET_FEATURE", .handler = do_set_feature, .perm_check = manager_or_root },
|
{ .cmd = KSU_IOCTL_SET_FEATURE, .name = "SET_FEATURE", .handler = do_set_feature, .perm_check = manager_or_root },
|
||||||
{ .cmd = KSU_IOCTL_GET_WRAPPER_FD, .name = "GET_WRAPPER_FD", .handler = do_get_wrapper_fd, .perm_check = manager_or_root },
|
{ .cmd = KSU_IOCTL_GET_WRAPPER_FD, .name = "GET_WRAPPER_FD", .handler = do_get_wrapper_fd, .perm_check = manager_or_root },
|
||||||
{ .cmd = KSU_IOCTL_MANAGE_MARK, .name = "MANAGE_MARK", .handler = do_manage_mark, .perm_check = manager_or_root },
|
{ .cmd = KSU_IOCTL_MANAGE_MARK, .name = "MANAGE_MARK", .handler = do_manage_mark, .perm_check = manager_or_root },
|
||||||
{ .cmd = KSU_IOCTL_NUKE_EXT4_SYSFS, .name = "NUKE_EXT4_SYSFS", .handler = do_nuke_ext4_sysfs, .perm_check = manager_or_root },
|
|
||||||
{ .cmd = KSU_IOCTL_ADD_TRY_UMOUNT, .name = "ADD_TRY_UMOUNT", .handler = add_try_umount, .perm_check = manager_or_root },
|
|
||||||
{ .cmd = KSU_IOCTL_GET_FULL_VERSION,.name = "GET_FULL_VERSION", .handler = do_get_full_version, .perm_check = always_allow},
|
{ .cmd = KSU_IOCTL_GET_FULL_VERSION,.name = "GET_FULL_VERSION", .handler = do_get_full_version, .perm_check = always_allow},
|
||||||
{ .cmd = KSU_IOCTL_HOOK_TYPE,.name = "GET_HOOK_TYPE", .handler = do_get_hook_type, .perm_check = manager_or_root},
|
{ .cmd = KSU_IOCTL_HOOK_TYPE,.name = "GET_HOOK_TYPE", .handler = do_get_hook_type, .perm_check = manager_or_root},
|
||||||
{ .cmd = KSU_IOCTL_ENABLE_KPM, .name = "GET_ENABLE_KPM", .handler = do_enable_kpm, .perm_check = manager_or_root},
|
{ .cmd = KSU_IOCTL_ENABLE_KPM, .name = "GET_ENABLE_KPM", .handler = do_enable_kpm, .perm_check = manager_or_root},
|
||||||
|
|
|
||||||
|
|
@ -94,21 +94,6 @@ struct ksu_manage_mark_cmd {
|
||||||
#define KSU_MARK_UNMARK 3
|
#define KSU_MARK_UNMARK 3
|
||||||
#define KSU_MARK_REFRESH 4
|
#define KSU_MARK_REFRESH 4
|
||||||
|
|
||||||
struct ksu_nuke_ext4_sysfs_cmd {
|
|
||||||
__aligned_u64 arg; // Input: mnt pointer
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ksu_add_try_umount_cmd {
|
|
||||||
__aligned_u64 arg; // char ptr, this is the mountpoint
|
|
||||||
__u32 flags; // this is the flag we use for it
|
|
||||||
__u8 mode; // denotes what to do with it 0:wipe_list 1:add_to_list 2:delete_entry
|
|
||||||
};
|
|
||||||
|
|
||||||
#define KSU_UMOUNT_WIPE 0 // ignore everything and wipe list
|
|
||||||
#define KSU_UMOUNT_ADD 1 // add entry (path + flags)
|
|
||||||
#define KSU_UMOUNT_DEL 2 // delete entry, strcmp
|
|
||||||
|
|
||||||
|
|
||||||
// Other command structures
|
// Other command structures
|
||||||
struct ksu_get_full_version_cmd {
|
struct ksu_get_full_version_cmd {
|
||||||
char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string
|
char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string
|
||||||
|
|
@ -162,8 +147,6 @@ struct ksu_manual_su_cmd {
|
||||||
#define KSU_IOCTL_SET_FEATURE _IOC(_IOC_WRITE, 'K', 14, 0)
|
#define KSU_IOCTL_SET_FEATURE _IOC(_IOC_WRITE, 'K', 14, 0)
|
||||||
#define KSU_IOCTL_GET_WRAPPER_FD _IOC(_IOC_WRITE, 'K', 15, 0)
|
#define KSU_IOCTL_GET_WRAPPER_FD _IOC(_IOC_WRITE, 'K', 15, 0)
|
||||||
#define KSU_IOCTL_MANAGE_MARK _IOC(_IOC_READ|_IOC_WRITE, 'K', 16, 0)
|
#define KSU_IOCTL_MANAGE_MARK _IOC(_IOC_READ|_IOC_WRITE, 'K', 16, 0)
|
||||||
#define KSU_IOCTL_NUKE_EXT4_SYSFS _IOC(_IOC_WRITE, 'K', 17, 0)
|
|
||||||
#define KSU_IOCTL_ADD_TRY_UMOUNT _IOC(_IOC_WRITE, 'K', 18, 0)
|
|
||||||
// Other IOCTL command definitions
|
// Other IOCTL command definitions
|
||||||
#define KSU_IOCTL_GET_FULL_VERSION _IOC(_IOC_READ, 'K', 100, 0)
|
#define KSU_IOCTL_GET_FULL_VERSION _IOC(_IOC_READ, 'K', 100, 0)
|
||||||
#define KSU_IOCTL_HOOK_TYPE _IOC(_IOC_READ, 'K', 101, 0)
|
#define KSU_IOCTL_HOOK_TYPE _IOC(_IOC_READ, 'K', 101, 0)
|
||||||
|
|
|
||||||
|
|
@ -268,7 +268,7 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||||
|
|
||||||
if (d_type == DT_DIR && my_ctx->depth > 0 &&
|
if (d_type == DT_DIR && my_ctx->depth > 0 &&
|
||||||
(my_ctx->stop && !*my_ctx->stop)) {
|
(my_ctx->stop && !*my_ctx->stop)) {
|
||||||
struct data_path *data = kzalloc(sizeof(struct data_path), GFP_ATOMIC);
|
struct data_path *data = kmalloc(sizeof(struct data_path), GFP_ATOMIC);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
pr_err("Failed to allocate memory for %s\n", dirpath);
|
pr_err("Failed to allocate memory for %s\n", dirpath);
|
||||||
|
|
@ -303,24 +303,29 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||||
// Check for dynamic sign or multi-manager signatures
|
// Check for dynamic sign or multi-manager signatures
|
||||||
if (is_multi_manager && (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2)) {
|
if (is_multi_manager && (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2)) {
|
||||||
crown_manager(dirpath, my_ctx->private_data, signature_index);
|
crown_manager(dirpath, my_ctx->private_data, signature_index);
|
||||||
|
|
||||||
|
struct apk_path_hash *apk_data = kmalloc(sizeof(struct apk_path_hash), GFP_ATOMIC);
|
||||||
|
if (apk_data) {
|
||||||
|
apk_data->hash = hash;
|
||||||
|
apk_data->exists = true;
|
||||||
|
list_add_tail(&apk_data->list, &apk_path_hash_list);
|
||||||
|
}
|
||||||
} else if (is_manager_apk(dirpath)) {
|
} else if (is_manager_apk(dirpath)) {
|
||||||
crown_manager(dirpath, my_ctx->private_data, 0);
|
crown_manager(dirpath, my_ctx->private_data, 0);
|
||||||
*my_ctx->stop = 1;
|
*my_ctx->stop = 1;
|
||||||
}
|
|
||||||
|
|
||||||
struct apk_path_hash *apk_data = kzalloc(sizeof(*apk_data), GFP_ATOMIC);
|
|
||||||
if (apk_data) {
|
|
||||||
apk_data->hash = hash;
|
|
||||||
apk_data->exists = true;
|
|
||||||
list_add_tail(&apk_data->list, &apk_path_hash_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_manager_apk(dirpath)) {
|
|
||||||
// Manager found, clear APK cache list
|
// Manager found, clear APK cache list
|
||||||
list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) {
|
list_for_each_entry_safe (pos, n, &apk_path_hash_list, list) {
|
||||||
list_del(&pos->list);
|
list_del(&pos->list);
|
||||||
kfree(pos);
|
kfree(pos);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
struct apk_path_hash *apk_data = kmalloc(sizeof(struct apk_path_hash), GFP_ATOMIC);
|
||||||
|
if (apk_data) {
|
||||||
|
apk_data->hash = hash;
|
||||||
|
apk_data->exists = true;
|
||||||
|
list_add_tail(&apk_data->list, &apk_path_hash_list);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ static struct umount_manager g_umount_mgr = {
|
||||||
|
|
||||||
static void try_umount_path(struct umount_entry *entry)
|
static void try_umount_path(struct umount_entry *entry)
|
||||||
{
|
{
|
||||||
try_umount(entry->path, entry->flags);
|
try_umount(entry->path, entry->check_mnt, entry->flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct umount_entry *find_entry_locked(const char *path)
|
static struct umount_entry *find_entry_locked(const char *path)
|
||||||
|
|
@ -33,12 +33,46 @@ static struct umount_entry *find_entry_locked(const char *path)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int init_default_entries(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
const struct {
|
||||||
|
const char *path;
|
||||||
|
bool check_mnt;
|
||||||
|
int flags;
|
||||||
|
} defaults[] = {
|
||||||
|
{ "/odm", true, 0 },
|
||||||
|
{ "/system", true, 0 },
|
||||||
|
{ "/vendor", true, 0 },
|
||||||
|
{ "/product", true, 0 },
|
||||||
|
{ "/system_ext", true, 0 },
|
||||||
|
{ "/data/adb/modules", false, MNT_DETACH },
|
||||||
|
{ "/debug_ramdisk", false, MNT_DETACH },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < ARRAY_SIZE(defaults); i++) {
|
||||||
|
ret = ksu_umount_manager_add(defaults[i].path,
|
||||||
|
defaults[i].check_mnt,
|
||||||
|
defaults[i].flags,
|
||||||
|
true); // is_default = true
|
||||||
|
if (ret) {
|
||||||
|
pr_err("Failed to add default entry: %s, ret=%d\n",
|
||||||
|
defaults[i].path, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("Initialized %zu default umount entries\n", ARRAY_SIZE(defaults));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int ksu_umount_manager_init(void)
|
int ksu_umount_manager_init(void)
|
||||||
{
|
{
|
||||||
INIT_LIST_HEAD(&g_umount_mgr.entry_list);
|
INIT_LIST_HEAD(&g_umount_mgr.entry_list);
|
||||||
spin_lock_init(&g_umount_mgr.lock);
|
spin_lock_init(&g_umount_mgr.lock);
|
||||||
|
|
||||||
return 0;
|
return init_default_entries();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_umount_manager_exit(void)
|
void ksu_umount_manager_exit(void)
|
||||||
|
|
@ -59,7 +93,7 @@ void ksu_umount_manager_exit(void)
|
||||||
pr_info("Umount manager cleaned up\n");
|
pr_info("Umount manager cleaned up\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int ksu_umount_manager_add(const char *path, int flags, bool is_default)
|
int ksu_umount_manager_add(const char *path, bool check_mnt, int flags, bool is_default)
|
||||||
{
|
{
|
||||||
struct umount_entry *entry;
|
struct umount_entry *entry;
|
||||||
unsigned long irqflags;
|
unsigned long irqflags;
|
||||||
|
|
@ -93,6 +127,7 @@ int ksu_umount_manager_add(const char *path, int flags, bool is_default)
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy(entry->path, path, sizeof(entry->path) - 1);
|
strncpy(entry->path, path, sizeof(entry->path) - 1);
|
||||||
|
entry->check_mnt = check_mnt;
|
||||||
entry->flags = flags;
|
entry->flags = flags;
|
||||||
entry->state = UMOUNT_STATE_IDLE;
|
entry->state = UMOUNT_STATE_IDLE;
|
||||||
entry->is_default = is_default;
|
entry->is_default = is_default;
|
||||||
|
|
@ -199,6 +234,7 @@ int ksu_umount_manager_get_entries(struct ksu_umount_entry_info __user *entries,
|
||||||
|
|
||||||
memset(&info, 0, sizeof(info));
|
memset(&info, 0, sizeof(info));
|
||||||
strncpy(info.path, entry->path, sizeof(info.path) - 1);
|
strncpy(info.path, entry->path, sizeof(info.path) - 1);
|
||||||
|
info.check_mnt = entry->check_mnt;
|
||||||
info.flags = entry->flags;
|
info.flags = entry->flags;
|
||||||
info.is_default = entry->is_default;
|
info.is_default = entry->is_default;
|
||||||
info.state = entry->state;
|
info.state = entry->state;
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ enum umount_entry_state {
|
||||||
struct umount_entry {
|
struct umount_entry {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
char path[256];
|
char path[256];
|
||||||
|
bool check_mnt;
|
||||||
int flags;
|
int flags;
|
||||||
enum umount_entry_state state;
|
enum umount_entry_state state;
|
||||||
bool is_default;
|
bool is_default;
|
||||||
|
|
@ -39,6 +40,7 @@ enum umount_manager_op {
|
||||||
struct ksu_umount_manager_cmd {
|
struct ksu_umount_manager_cmd {
|
||||||
__u32 operation;
|
__u32 operation;
|
||||||
char path[256];
|
char path[256];
|
||||||
|
__u8 check_mnt;
|
||||||
__s32 flags;
|
__s32 flags;
|
||||||
__u32 count;
|
__u32 count;
|
||||||
__aligned_u64 entries_ptr;
|
__aligned_u64 entries_ptr;
|
||||||
|
|
@ -46,6 +48,7 @@ struct ksu_umount_manager_cmd {
|
||||||
|
|
||||||
struct ksu_umount_entry_info {
|
struct ksu_umount_entry_info {
|
||||||
char path[256];
|
char path[256];
|
||||||
|
__u8 check_mnt;
|
||||||
__s32 flags;
|
__s32 flags;
|
||||||
__u8 is_default;
|
__u8 is_default;
|
||||||
__u32 state;
|
__u32 state;
|
||||||
|
|
@ -54,7 +57,7 @@ struct ksu_umount_entry_info {
|
||||||
|
|
||||||
int ksu_umount_manager_init(void);
|
int ksu_umount_manager_init(void);
|
||||||
void ksu_umount_manager_exit(void);
|
void ksu_umount_manager_exit(void);
|
||||||
int ksu_umount_manager_add(const char *path, int flags, bool is_default);
|
int ksu_umount_manager_add(const char *path, bool check_mnt, int flags, bool is_default);
|
||||||
int ksu_umount_manager_remove(const char *path);
|
int ksu_umount_manager_remove(const char *path);
|
||||||
void ksu_umount_manager_execute_all(const struct cred *cred);
|
void ksu_umount_manager_execute_all(const struct cred *cred);
|
||||||
int ksu_umount_manager_get_entries(struct ksu_umount_entry_info __user *entries, u32 *count);
|
int ksu_umount_manager_get_entries(struct ksu_umount_entry_info __user *entries, u32 *count);
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -319,14 +319,6 @@ NativeBridge(setEnhancedSecurityEnabled, jboolean, jboolean enabled) {
|
||||||
return set_enhanced_security_enabled(enabled);
|
return set_enhanced_security_enabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeBridgeNP(isSuLogEnabled, jboolean) {
|
|
||||||
return is_sulog_enabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
NativeBridge(setSuLogEnabled, jboolean, jboolean enabled) {
|
|
||||||
return set_sulog_enabled(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
NativeBridge(getUserName, jstring, jint uid) {
|
NativeBridge(getUserName, jstring, jint uid) {
|
||||||
struct passwd *pw = getpwuid((uid_t) uid);
|
struct passwd *pw = getpwuid((uid_t) uid);
|
||||||
if (pw && pw->pw_name && pw->pw_name[0] != '\0') {
|
if (pw && pw->pw_name && pw->pw_name[0] != '\0') {
|
||||||
|
|
|
||||||
|
|
@ -231,22 +231,6 @@ bool is_enhanced_security_enabled() {
|
||||||
return value != 0;
|
return value != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool set_sulog_enabled(bool enabled) {
|
|
||||||
return set_feature(KSU_FEATURE_SULOG, enabled ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_sulog_enabled() {
|
|
||||||
uint64_t value = 0;
|
|
||||||
bool supported = false;
|
|
||||||
if (!get_feature(KSU_FEATURE_SULOG, &value, &supported)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!supported) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return value != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_full_version(char* buff) {
|
void get_full_version(char* buff) {
|
||||||
struct ksu_get_full_version_cmd cmd = {0};
|
struct ksu_get_full_version_cmd cmd = {0};
|
||||||
if (ksuctl(KSU_IOCTL_GET_FULL_VERSION, &cmd) == 0) {
|
if (ksuctl(KSU_IOCTL_GET_FULL_VERSION, &cmd) == 0) {
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,6 @@ enum ksu_feature_id {
|
||||||
KSU_FEATURE_SU_COMPAT = 0,
|
KSU_FEATURE_SU_COMPAT = 0,
|
||||||
KSU_FEATURE_KERNEL_UMOUNT = 1,
|
KSU_FEATURE_KERNEL_UMOUNT = 1,
|
||||||
KSU_FEATURE_ENHANCED_SECURITY = 2,
|
KSU_FEATURE_ENHANCED_SECURITY = 2,
|
||||||
KSU_FEATURE_SULOG = 3,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generic feature API
|
// Generic feature API
|
||||||
|
|
@ -212,11 +211,8 @@ bool is_kernel_umount_enabled();
|
||||||
|
|
||||||
// Enhanced security
|
// Enhanced security
|
||||||
bool set_enhanced_security_enabled(bool enabled);
|
bool set_enhanced_security_enabled(bool enabled);
|
||||||
bool is_enhanced_security_enabled();
|
|
||||||
|
|
||||||
// Su log
|
bool is_enhanced_security_enabled();
|
||||||
bool set_sulog_enabled(bool enabled);
|
|
||||||
bool is_sulog_enabled();
|
|
||||||
|
|
||||||
// Other command structures
|
// Other command structures
|
||||||
struct ksu_get_full_version_cmd {
|
struct ksu_get_full_version_cmd {
|
||||||
|
|
|
||||||
|
|
@ -118,15 +118,6 @@ object Natives {
|
||||||
external fun isEnhancedSecurityEnabled(): Boolean
|
external fun isEnhancedSecurityEnabled(): Boolean
|
||||||
external fun setEnhancedSecurityEnabled(enabled: Boolean): Boolean
|
external fun setEnhancedSecurityEnabled(enabled: Boolean): Boolean
|
||||||
|
|
||||||
/**
|
|
||||||
* Su Log can be enabled/disabled.
|
|
||||||
* 0: disabled
|
|
||||||
* 1: enabled
|
|
||||||
* negative : error
|
|
||||||
*/
|
|
||||||
external fun isSuLogEnabled(): Boolean
|
|
||||||
external fun setSuLogEnabled(enabled: Boolean): Boolean
|
|
||||||
|
|
||||||
external fun isKPMEnabled(): Boolean
|
external fun isKPMEnabled(): Boolean
|
||||||
external fun getHookType(): String
|
external fun getHookType(): String
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -353,40 +353,38 @@ fun InstallScreen(
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
) {
|
) {
|
||||||
if (isGKI) {
|
// 使用本地的LKM文件
|
||||||
// 使用本地的LKM文件
|
ElevatedCard(
|
||||||
ElevatedCard(
|
colors = getCardColors(MaterialTheme.colorScheme.surfaceVariant),
|
||||||
colors = getCardColors(MaterialTheme.colorScheme.surfaceVariant),
|
elevation = getCardElevation(),
|
||||||
elevation = getCardElevation(),
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 12.dp),
|
||||||
|
) {
|
||||||
|
ListItem(
|
||||||
|
headlineContent = {
|
||||||
|
Text(stringResource(id = R.string.install_upload_lkm_file))
|
||||||
|
},
|
||||||
|
supportingContent = {
|
||||||
|
(lkmSelection as? LkmSelection.LkmUri)?.let {
|
||||||
|
Text(
|
||||||
|
stringResource(
|
||||||
|
id = R.string.selected_lkm,
|
||||||
|
it.uri.lastPathSegment ?: "(file)"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
leadingContent = {
|
||||||
|
Icon(
|
||||||
|
Icons.AutoMirrored.Filled.Input,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(bottom = 12.dp),
|
.clickable { onLkmUpload() }
|
||||||
) {
|
)
|
||||||
ListItem(
|
|
||||||
headlineContent = {
|
|
||||||
Text(stringResource(id = R.string.install_upload_lkm_file))
|
|
||||||
},
|
|
||||||
supportingContent = {
|
|
||||||
(lkmSelection as? LkmSelection.LkmUri)?.let {
|
|
||||||
Text(
|
|
||||||
stringResource(
|
|
||||||
id = R.string.selected_lkm,
|
|
||||||
it.uri.lastPathSegment ?: "(file)"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
leadingContent = {
|
|
||||||
Icon(
|
|
||||||
Icons.AutoMirrored.Filled.Input,
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
},
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.clickable { onLkmUpload() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(installMethod as? InstallMethod.HorizonKernel)?.let { method ->
|
(installMethod as? InstallMethod.HorizonKernel)?.let { method ->
|
||||||
|
|
|
||||||
|
|
@ -865,7 +865,7 @@ private fun ModuleList(
|
||||||
ModuleOperationUtils.handleModuleUninstall(module.dirId)
|
ModuleOperationUtils.handleModuleUninstall(module.dirId)
|
||||||
uninstallModule(module.dirId)
|
uninstallModule(module.dirId)
|
||||||
} else {
|
} else {
|
||||||
undoUninstallModule(module.dirId)
|
restoreModule(module.dirId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,12 @@ import androidx.compose.animation.*
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
||||||
import androidx.compose.material.icons.automirrored.filled.Undo
|
import androidx.compose.material.icons.automirrored.filled.Undo
|
||||||
import androidx.compose.material.icons.filled.*
|
import androidx.compose.material.icons.filled.*
|
||||||
import androidx.compose.material.icons.rounded.EnhancedEncryption
|
import androidx.compose.material.icons.rounded.EnhancedEncryption
|
||||||
|
|
@ -54,6 +56,7 @@ import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha
|
||||||
import com.sukisu.ultra.ui.theme.getCardColors
|
import com.sukisu.ultra.ui.theme.getCardColors
|
||||||
import com.sukisu.ultra.ui.theme.getCardElevation
|
import com.sukisu.ultra.ui.theme.getCardElevation
|
||||||
import com.sukisu.ultra.ui.util.*
|
import com.sukisu.ultra.ui.util.*
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
@ -76,7 +79,6 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||||
val snackBarHost = LocalSnackbarHost.current
|
val snackBarHost = LocalSnackbarHost.current
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||||
var isSuLogEnabled by remember { mutableStateOf(Natives.isSuLogEnabled()) }
|
|
||||||
var selectedEngine by rememberSaveable {
|
var selectedEngine by rememberSaveable {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
prefs.getString("webui_engine", "default") ?: "default"
|
prefs.getString("webui_engine", "default") ?: "default"
|
||||||
|
|
@ -148,28 +150,11 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val enhancedStatus by produceState(initialValue = "") {
|
|
||||||
value = getFeatureStatus("enhanced_security")
|
|
||||||
}
|
|
||||||
val enhancedSummary = when (enhancedStatus) {
|
|
||||||
"unsupported" -> stringResource(id = R.string.feature_status_unsupported_summary)
|
|
||||||
"managed" -> stringResource(id = R.string.feature_status_managed_summary)
|
|
||||||
else -> stringResource(id = R.string.settings_enable_enhanced_security_summary)
|
|
||||||
}
|
|
||||||
SuperDropdown(
|
SuperDropdown(
|
||||||
icon = Icons.Rounded.EnhancedEncryption,
|
icon = Icons.Rounded.EnhancedEncryption,
|
||||||
title = stringResource(id = R.string.settings_enable_enhanced_security),
|
title = stringResource(id = R.string.settings_enable_enhanced_security),
|
||||||
summary = enhancedSummary,
|
summary = stringResource(id = R.string.settings_enable_enhanced_security_summary),
|
||||||
items = modeItems,
|
items = modeItems,
|
||||||
leftAction = {
|
|
||||||
Icon(
|
|
||||||
Icons.Rounded.EnhancedEncryption,
|
|
||||||
modifier = Modifier.padding(end = 16.dp),
|
|
||||||
contentDescription = stringResource(id = R.string.settings_enable_enhanced_security),
|
|
||||||
tint = MaterialTheme.colorScheme.onBackground
|
|
||||||
)
|
|
||||||
},
|
|
||||||
enabled = enhancedStatus == "supported",
|
|
||||||
selectedIndex = enhancedSecurityMode,
|
selectedIndex = enhancedSecurityMode,
|
||||||
onSelectedIndexChange = { index ->
|
onSelectedIndexChange = { index ->
|
||||||
when (index) {
|
when (index) {
|
||||||
|
|
@ -208,28 +193,11 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val suStatus by produceState(initialValue = "") {
|
|
||||||
value = getFeatureStatus("su_compat")
|
|
||||||
}
|
|
||||||
val suSummary = when (suStatus) {
|
|
||||||
"unsupported" -> stringResource(id = R.string.feature_status_unsupported_summary)
|
|
||||||
"managed" -> stringResource(id = R.string.feature_status_managed_summary)
|
|
||||||
else -> stringResource(id = R.string.settings_disable_su_summary)
|
|
||||||
}
|
|
||||||
SuperDropdown(
|
SuperDropdown(
|
||||||
icon = Icons.Rounded.RemoveModerator,
|
icon = Icons.Rounded.RemoveModerator,
|
||||||
title = stringResource(id = R.string.settings_disable_su),
|
title = stringResource(id = R.string.settings_disable_su),
|
||||||
summary = suSummary,
|
summary = stringResource(id = R.string.settings_disable_su_summary),
|
||||||
items = modeItems,
|
items = modeItems,
|
||||||
leftAction = {
|
|
||||||
Icon(
|
|
||||||
Icons.Rounded.RemoveModerator,
|
|
||||||
modifier = Modifier.padding(end = 16.dp),
|
|
||||||
contentDescription = stringResource(id = R.string.settings_disable_su),
|
|
||||||
tint = MaterialTheme.colorScheme.onBackground
|
|
||||||
)
|
|
||||||
},
|
|
||||||
enabled = suStatus == "supported",
|
|
||||||
selectedIndex = suCompatMode,
|
selectedIndex = suCompatMode,
|
||||||
onSelectedIndexChange = { index ->
|
onSelectedIndexChange = { index ->
|
||||||
when (index) {
|
when (index) {
|
||||||
|
|
@ -268,28 +236,11 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val umountStatus by produceState(initialValue = "") {
|
|
||||||
value = getFeatureStatus("kernel_umount")
|
|
||||||
}
|
|
||||||
val umountSummary = when (umountStatus) {
|
|
||||||
"unsupported" -> stringResource(id = R.string.feature_status_unsupported_summary)
|
|
||||||
"managed" -> stringResource(id = R.string.feature_status_managed_summary)
|
|
||||||
else -> stringResource(id = R.string.settings_disable_kernel_umount_summary)
|
|
||||||
}
|
|
||||||
SuperDropdown(
|
SuperDropdown(
|
||||||
icon = Icons.Rounded.RemoveCircle,
|
icon = Icons.Rounded.RemoveCircle,
|
||||||
title = stringResource(id = R.string.settings_disable_kernel_umount),
|
title = stringResource(id = R.string.settings_disable_kernel_umount),
|
||||||
summary = umountSummary,
|
summary = stringResource(id = R.string.settings_disable_kernel_umount_summary),
|
||||||
items = modeItems,
|
items = modeItems,
|
||||||
leftAction = {
|
|
||||||
Icon(
|
|
||||||
Icons.Rounded.RemoveCircle,
|
|
||||||
modifier = Modifier.padding(end = 16.dp),
|
|
||||||
contentDescription = stringResource(id = R.string.settings_disable_kernel_umount),
|
|
||||||
tint = MaterialTheme.colorScheme.onBackground
|
|
||||||
)
|
|
||||||
},
|
|
||||||
enabled = umountStatus == "supported",
|
|
||||||
selectedIndex = kernelUmountMode,
|
selectedIndex = kernelUmountMode,
|
||||||
onSelectedIndexChange = { index ->
|
onSelectedIndexChange = { index ->
|
||||||
when (index) {
|
when (index) {
|
||||||
|
|
@ -319,68 +270,6 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var suLogMode by rememberSaveable {
|
|
||||||
mutableIntStateOf(
|
|
||||||
run {
|
|
||||||
val currentEnabled = Natives.isSuLogEnabled()
|
|
||||||
val savedPersist = prefs.getInt("sulog_mode", 0)
|
|
||||||
if (savedPersist == 2) 2 else if (!currentEnabled) 1 else 0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val suLogStatus by produceState(initialValue = "") {
|
|
||||||
value = getFeatureStatus("sulog")
|
|
||||||
}
|
|
||||||
val suLogSummary = when (suLogStatus) {
|
|
||||||
"unsupported" -> stringResource(id = R.string.feature_status_unsupported_summary)
|
|
||||||
"managed" -> stringResource(id = R.string.feature_status_managed_summary)
|
|
||||||
else -> stringResource(id = R.string.settings_disable_sulog_summary)
|
|
||||||
}
|
|
||||||
SuperDropdown(
|
|
||||||
title = stringResource(id = R.string.settings_disable_sulog),
|
|
||||||
summary = suLogSummary,
|
|
||||||
items = modeItems,
|
|
||||||
leftAction = {
|
|
||||||
Icon(
|
|
||||||
Icons.Rounded.RemoveCircle,
|
|
||||||
modifier = Modifier.padding(end = 16.dp),
|
|
||||||
contentDescription = stringResource(id = R.string.settings_disable_sulog),
|
|
||||||
tint = MaterialTheme.colorScheme.onBackground
|
|
||||||
)
|
|
||||||
},
|
|
||||||
enabled = suLogStatus == "supported",
|
|
||||||
selectedIndex = suLogMode,
|
|
||||||
onSelectedIndexChange = { index ->
|
|
||||||
when (index) {
|
|
||||||
// Default: enable and save to persist
|
|
||||||
0 -> if (Natives.setSuLogEnabled(true)) {
|
|
||||||
execKsud("feature save", true)
|
|
||||||
prefs.edit { putInt("sulog_mode", 0) }
|
|
||||||
suLogMode = 0
|
|
||||||
isSuLogEnabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporarily disable: save enabled state first, then disable
|
|
||||||
1 -> if (Natives.setSuLogEnabled(true)) {
|
|
||||||
execKsud("feature save", true)
|
|
||||||
if (Natives.setSuLogEnabled(false)) {
|
|
||||||
prefs.edit { putInt("sulog_mode", 0) }
|
|
||||||
suLogMode = 1
|
|
||||||
isSuLogEnabled = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Permanently disable: disable and save
|
|
||||||
2 -> if (Natives.setSuLogEnabled(false)) {
|
|
||||||
execKsud("feature save", true)
|
|
||||||
prefs.edit { putInt("sulog_mode", 2) }
|
|
||||||
suLogMode = 2
|
|
||||||
isSuLogEnabled = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// 卸载模块开关
|
// 卸载模块开关
|
||||||
var umountChecked by rememberSaveable { mutableStateOf(Natives.isDefaultUmountModules()) }
|
var umountChecked by rememberSaveable { mutableStateOf(Natives.isDefaultUmountModules()) }
|
||||||
SwitchItem(
|
SwitchItem(
|
||||||
|
|
@ -394,6 +283,26 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// 强制签名验证开关
|
||||||
|
var forceSignatureVerification by rememberSaveable {
|
||||||
|
mutableStateOf(prefs.getBoolean("force_signature_verification", false))
|
||||||
|
}
|
||||||
|
SwitchItem(
|
||||||
|
icon = Icons.Filled.Security,
|
||||||
|
title = stringResource(R.string.module_signature_verification),
|
||||||
|
summary = stringResource(R.string.module_signature_verification_summary),
|
||||||
|
checked = forceSignatureVerification,
|
||||||
|
onCheckedChange = { enabled ->
|
||||||
|
prefs.edit { putBoolean("force_signature_verification", enabled) }
|
||||||
|
forceSignatureVerification = enabled
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// UID 扫描开关
|
||||||
|
if (Natives.version >= Natives.MINIMAL_SUPPORTED_UID_SCANNER && Natives.version >= Natives.MINIMAL_NEW_IOCTL_KERNEL) {
|
||||||
|
UidScannerSection(prefs, snackBarHost, scope, context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -494,16 +403,14 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||||
|
|
||||||
// 查看使用日志
|
// 查看使用日志
|
||||||
KsuIsValid {
|
KsuIsValid {
|
||||||
if (isSuLogEnabled) {
|
SettingItem(
|
||||||
SettingItem(
|
icon = Icons.Filled.Visibility,
|
||||||
icon = Icons.Filled.Visibility,
|
title = stringResource(R.string.log_viewer_view_logs),
|
||||||
title = stringResource(R.string.log_viewer_view_logs),
|
summary = stringResource(R.string.log_viewer_view_logs_summary),
|
||||||
summary = stringResource(R.string.log_viewer_view_logs_summary),
|
onClick = {
|
||||||
onClick = {
|
navigator.navigate(LogViewerScreenDestination)
|
||||||
navigator.navigate(LogViewerScreenDestination)
|
}
|
||||||
}
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
val lkmMode = Natives.isLkmMode
|
val lkmMode = Natives.isLkmMode
|
||||||
KsuIsValid {
|
KsuIsValid {
|
||||||
|
|
@ -1049,3 +956,124 @@ private fun TopBar(
|
||||||
scrollBehavior = scrollBehavior
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun UidScannerSection(
|
||||||
|
prefs: SharedPreferences,
|
||||||
|
snackBarHost: SnackbarHostState,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
context: Context
|
||||||
|
) {
|
||||||
|
if (Natives.version < Natives.MINIMAL_SUPPORTED_UID_SCANNER) return
|
||||||
|
|
||||||
|
val realAuto = Natives.isUidScannerEnabled()
|
||||||
|
val realMulti = getUidMultiUserScan()
|
||||||
|
|
||||||
|
var autoOn by remember { mutableStateOf(realAuto) }
|
||||||
|
var multiOn by remember { mutableStateOf(realMulti) }
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
autoOn = realAuto
|
||||||
|
multiOn = realMulti
|
||||||
|
prefs.edit {
|
||||||
|
putBoolean("uid_auto_scan", autoOn)
|
||||||
|
putBoolean("uid_multi_user_scan", multiOn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchItem(
|
||||||
|
icon = Icons.Filled.Scanner,
|
||||||
|
title = stringResource(R.string.uid_auto_scan_title),
|
||||||
|
summary = stringResource(R.string.uid_auto_scan_summary),
|
||||||
|
checked = autoOn,
|
||||||
|
onCheckedChange = { target ->
|
||||||
|
autoOn = target
|
||||||
|
if (!target) multiOn = false
|
||||||
|
|
||||||
|
scope.launch(Dispatchers.IO) {
|
||||||
|
setUidAutoScan(target)
|
||||||
|
val actual = Natives.isUidScannerEnabled() || readUidScannerFile()
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
autoOn = actual
|
||||||
|
if (!actual) multiOn = false
|
||||||
|
prefs.edit {
|
||||||
|
putBoolean("uid_auto_scan", actual)
|
||||||
|
putBoolean("uid_multi_user_scan", multiOn)
|
||||||
|
}
|
||||||
|
if (actual != target) {
|
||||||
|
snackBarHost.showSnackbar(
|
||||||
|
context.getString(R.string.uid_scanner_setting_failed)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = autoOn,
|
||||||
|
enter = fadeIn() + expandVertically(),
|
||||||
|
exit = fadeOut() + shrinkVertically()
|
||||||
|
) {
|
||||||
|
SwitchItem(
|
||||||
|
icon = Icons.Filled.Groups,
|
||||||
|
title = stringResource(R.string.uid_multi_user_scan_title),
|
||||||
|
summary = stringResource(R.string.uid_multi_user_scan_summary),
|
||||||
|
checked = multiOn,
|
||||||
|
onCheckedChange = { target ->
|
||||||
|
scope.launch(Dispatchers.IO) {
|
||||||
|
val ok = setUidMultiUserScan(target)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
if (ok) {
|
||||||
|
multiOn = target
|
||||||
|
prefs.edit { putBoolean("uid_multi_user_scan", target) }
|
||||||
|
} else {
|
||||||
|
snackBarHost.showSnackbar(
|
||||||
|
context.getString(R.string.uid_scanner_setting_failed)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = autoOn,
|
||||||
|
enter = fadeIn() + expandVertically(),
|
||||||
|
exit = fadeOut() + shrinkVertically()
|
||||||
|
) {
|
||||||
|
val confirmDialog = rememberConfirmDialog()
|
||||||
|
SettingItem(
|
||||||
|
icon = Icons.Filled.CleaningServices,
|
||||||
|
title = stringResource(R.string.clean_runtime_environment),
|
||||||
|
summary = stringResource(R.string.clean_runtime_environment_summary),
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
if (confirmDialog.awaitConfirm(
|
||||||
|
title = context.getString(R.string.clean_runtime_environment),
|
||||||
|
content = context.getString(R.string.clean_runtime_environment_confirm)
|
||||||
|
) == ConfirmResult.Confirmed
|
||||||
|
) {
|
||||||
|
if (cleanRuntimeEnvironment()) {
|
||||||
|
autoOn = false
|
||||||
|
multiOn = false
|
||||||
|
prefs.edit {
|
||||||
|
putBoolean("uid_auto_scan", false)
|
||||||
|
putBoolean("uid_multi_user_scan", false)
|
||||||
|
}
|
||||||
|
Natives.setUidScannerEnabled(false)
|
||||||
|
snackBarHost.showSnackbar(
|
||||||
|
context.getString(R.string.clean_runtime_environment_success)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
snackBarHost.showSnackbar(
|
||||||
|
context.getString(R.string.clean_runtime_environment_failed)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import androidx.compose.animation.core.*
|
||||||
import androidx.compose.animation.expandHorizontally
|
import androidx.compose.animation.expandHorizontally
|
||||||
import androidx.compose.animation.expandVertically
|
import androidx.compose.animation.expandVertically
|
||||||
import androidx.compose.animation.*
|
import androidx.compose.animation.*
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
|
@ -32,7 +31,6 @@ import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
import androidx.compose.material3.rememberTopAppBarState
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.MutableState
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
|
@ -40,15 +38,12 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.rotate
|
|
||||||
import androidx.compose.ui.draw.scale
|
import androidx.compose.ui.draw.scale
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.layout.ContentScale
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
|
@ -56,7 +51,6 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import coil.compose.rememberAsyncImagePainter
|
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import com.dergoogler.mmrl.ui.component.LabelItem
|
import com.dergoogler.mmrl.ui.component.LabelItem
|
||||||
import com.dergoogler.mmrl.ui.component.LabelItemDefaults
|
import com.dergoogler.mmrl.ui.component.LabelItemDefaults
|
||||||
|
|
@ -320,9 +314,6 @@ private fun SuperUserContent(
|
||||||
scope: CoroutineScope
|
scope: CoroutineScope
|
||||||
) {
|
) {
|
||||||
val expandedGroups = remember { mutableStateOf(setOf<Int>()) }
|
val expandedGroups = remember { mutableStateOf(setOf<Int>()) }
|
||||||
val density = LocalDensity.current
|
|
||||||
val targetSizePx = remember(density) { with(density) { 36.dp.roundToPx() } }
|
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
PullToRefreshBox(
|
PullToRefreshBox(
|
||||||
modifier = Modifier.padding(innerPadding),
|
modifier = Modifier.padding(innerPadding),
|
||||||
|
|
@ -338,7 +329,6 @@ private fun SuperUserContent(
|
||||||
filteredAndSortedAppGroups.forEachIndexed { _, appGroup ->
|
filteredAndSortedAppGroups.forEachIndexed { _, appGroup ->
|
||||||
item(key = "${appGroup.uid}-${appGroup.mainApp.packageName}") {
|
item(key = "${appGroup.uid}-${appGroup.mainApp.packageName}") {
|
||||||
AppGroupItem(
|
AppGroupItem(
|
||||||
expandedGroups = expandedGroups,
|
|
||||||
appGroup = appGroup,
|
appGroup = appGroup,
|
||||||
isSelected = appGroup.packageNames.any { viewModel.selectedApps.contains(it) },
|
isSelected = appGroup.packageNames.any { viewModel.selectedApps.contains(it) },
|
||||||
onToggleSelection = {
|
onToggleSelection = {
|
||||||
|
|
@ -367,46 +357,32 @@ private fun SuperUserContent(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appGroup.apps.size <= 1) return@forEachIndexed
|
|
||||||
|
|
||||||
items(appGroup.apps, key = { "${it.packageName}-${it.uid}" }) { app ->
|
items(appGroup.apps, key = { "${it.packageName}-${it.uid}" }) { app ->
|
||||||
val painter = rememberAsyncImagePainter(
|
|
||||||
model = ImageRequest.Builder(context)
|
|
||||||
.data(app.packageInfo)
|
|
||||||
.size(targetSizePx)
|
|
||||||
.crossfade(true)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
|
|
||||||
val listItemContent = remember(app.packageName, appGroup.uid) {
|
|
||||||
@Composable {
|
|
||||||
ListItem(
|
|
||||||
modifier = Modifier
|
|
||||||
.clickable { navigator.navigate(AppProfileScreenDestination(app)) }
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(start = 10.dp),
|
|
||||||
headlineContent = { Text(app.label, style = MaterialTheme.typography.bodyMedium) },
|
|
||||||
supportingContent = { Text(app.packageName, style = MaterialTheme.typography.bodySmall) },
|
|
||||||
leadingContent = {
|
|
||||||
Image(
|
|
||||||
painter = painter,
|
|
||||||
contentDescription = app.label,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(4.dp)
|
|
||||||
.size(36.dp),
|
|
||||||
contentScale = ContentScale.Crop
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = expandedGroups.value.contains(appGroup.uid),
|
visible = expandedGroups.value.contains(appGroup.uid) && appGroup.apps.size > 1,
|
||||||
enter = fadeIn() + expandVertically(),
|
enter = fadeIn() + expandVertically(),
|
||||||
exit = fadeOut() + shrinkVertically()
|
exit = fadeOut() + shrinkVertically()
|
||||||
) {
|
) {
|
||||||
listItemContent()
|
ListItem(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(start = 10.dp)
|
||||||
|
.clickable {
|
||||||
|
navigator.navigate(AppProfileScreenDestination(app))
|
||||||
|
},
|
||||||
|
headlineContent = { Text(app.label, style = MaterialTheme.typography.bodyMedium) },
|
||||||
|
supportingContent = { Text(app.packageName, style = MaterialTheme.typography.bodySmall) },
|
||||||
|
leadingContent = {
|
||||||
|
AsyncImage(
|
||||||
|
model = ImageRequest.Builder(LocalContext.current)
|
||||||
|
.data(app.packageInfo)
|
||||||
|
.crossfade(true)
|
||||||
|
.build(),
|
||||||
|
contentDescription = app.label,
|
||||||
|
modifier = Modifier.padding(4.dp).width(36.dp).height(36.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -821,8 +797,7 @@ private fun AppGroupItem(
|
||||||
onToggleSelection: () -> Unit,
|
onToggleSelection: () -> Unit,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
onLongClick: () -> Unit,
|
onLongClick: () -> Unit,
|
||||||
viewModel: SuperUserViewModel,
|
viewModel: SuperUserViewModel
|
||||||
expandedGroups: MutableState<Set<Int>>
|
|
||||||
) {
|
) {
|
||||||
val mainApp = appGroup.mainApp
|
val mainApp = appGroup.mainApp
|
||||||
|
|
||||||
|
|
@ -843,27 +818,9 @@ private fun AppGroupItem(
|
||||||
} else {
|
} else {
|
||||||
mainApp.packageName
|
mainApp.packageName
|
||||||
}
|
}
|
||||||
|
Text(summaryText)
|
||||||
|
|
||||||
Row(
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
|
||||||
) {
|
|
||||||
Text(summaryText)
|
|
||||||
|
|
||||||
if (appGroup.apps.size > 1) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.KeyboardArrowDown,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.rotate(
|
|
||||||
animateFloatAsState(
|
|
||||||
targetValue = if (expandedGroups.value.contains(appGroup.uid)) 180f else 0f,
|
|
||||||
animationSpec = tween(200, easing = LinearOutSlowInEasing),
|
|
||||||
label = ""
|
|
||||||
).value
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
FlowRow(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||||
if (appGroup.allowSu) {
|
if (appGroup.allowSu) {
|
||||||
|
|
@ -896,7 +853,7 @@ private fun AppGroupItem(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (appGroup.apps.size > 1) {
|
if (appGroup.apps.size > 1) {
|
||||||
appGroup.userName?.let {
|
Natives.getUserName(appGroup.uid)?.let {
|
||||||
LabelItem(
|
LabelItem(
|
||||||
text = it,
|
text = it,
|
||||||
style = LabelItemDefaults.style.copy(
|
style = LabelItemDefaults.style.copy(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.sukisu.ultra.ui.screen
|
package com.sukisu.ultra.ui.screen
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
|
@ -38,7 +37,9 @@ private val SPACING_LARGE = 16.dp
|
||||||
|
|
||||||
data class UmountPathEntry(
|
data class UmountPathEntry(
|
||||||
val path: String,
|
val path: String,
|
||||||
|
val checkMnt: Boolean,
|
||||||
val flags: Int,
|
val flags: Int,
|
||||||
|
val isDefault: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
|
@ -242,11 +243,11 @@ fun UmountManagerScreen(navigator: DestinationsNavigator) {
|
||||||
if (showAddDialog) {
|
if (showAddDialog) {
|
||||||
AddUmountPathDialog(
|
AddUmountPathDialog(
|
||||||
onDismiss = { showAddDialog = false },
|
onDismiss = { showAddDialog = false },
|
||||||
onConfirm = { path, flags ->
|
onConfirm = { path, checkMnt, flags ->
|
||||||
showAddDialog = false
|
showAddDialog = false
|
||||||
|
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
val success = addUmountPath(path, flags)
|
val success = addUmountPath(path, checkMnt, flags)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
if (success) {
|
if (success) {
|
||||||
saveUmountConfig()
|
saveUmountConfig()
|
||||||
|
|
@ -290,7 +291,10 @@ fun UmountPathCard(
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.Folder,
|
imageVector = Icons.Filled.Folder,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
tint = if (entry.isDefault)
|
||||||
|
MaterialTheme.colorScheme.primary
|
||||||
|
else
|
||||||
|
MaterialTheme.colorScheme.secondary,
|
||||||
modifier = Modifier.size(24.dp)
|
modifier = Modifier.size(24.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -304,31 +308,42 @@ fun UmountPathCard(
|
||||||
Spacer(modifier = Modifier.height(SPACING_SMALL))
|
Spacer(modifier = Modifier.height(SPACING_SMALL))
|
||||||
Text(
|
Text(
|
||||||
text = buildString {
|
text = buildString {
|
||||||
|
append(context.getString(R.string.check_mount_type))
|
||||||
|
append(": ")
|
||||||
|
append(if (entry.checkMnt) context.getString(R.string.yes) else context.getString(R.string.no))
|
||||||
|
append(" | ")
|
||||||
append(context.getString(R.string.flags))
|
append(context.getString(R.string.flags))
|
||||||
append(": ")
|
append(": ")
|
||||||
append(entry.flags.toUmountFlagName(context))
|
append(entry.flags.toUmountFlagName(context))
|
||||||
|
if (entry.isDefault) {
|
||||||
|
append(" | ")
|
||||||
|
append(context.getString(R.string.default_entry))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
IconButton(
|
|
||||||
onClick = {
|
if (!entry.isDefault) {
|
||||||
scope.launch {
|
IconButton(
|
||||||
if (confirmDialog.awaitConfirm(
|
onClick = {
|
||||||
title = context.getString(R.string.confirm_delete),
|
scope.launch {
|
||||||
content = context.getString(R.string.confirm_delete_umount_path, entry.path)
|
if (confirmDialog.awaitConfirm(
|
||||||
) == ConfirmResult.Confirmed) {
|
title = context.getString(R.string.confirm_delete),
|
||||||
onDelete()
|
content = context.getString(R.string.confirm_delete_umount_path, entry.path)
|
||||||
|
) == ConfirmResult.Confirmed) {
|
||||||
|
onDelete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Delete,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
}
|
}
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Filled.Delete,
|
|
||||||
contentDescription = null,
|
|
||||||
tint = MaterialTheme.colorScheme.error
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -337,9 +352,10 @@ fun UmountPathCard(
|
||||||
@Composable
|
@Composable
|
||||||
fun AddUmountPathDialog(
|
fun AddUmountPathDialog(
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
onConfirm: (String, Int) -> Unit
|
onConfirm: (String, Boolean, Int) -> Unit
|
||||||
) {
|
) {
|
||||||
var path by rememberSaveable { mutableStateOf("") }
|
var path by rememberSaveable { mutableStateOf("") }
|
||||||
|
var checkMnt by rememberSaveable { mutableStateOf(false) }
|
||||||
var flags by rememberSaveable { mutableStateOf("-1") }
|
var flags by rememberSaveable { mutableStateOf("-1") }
|
||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
|
|
@ -357,6 +373,20 @@ fun AddUmountPathDialog(
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(SPACING_MEDIUM))
|
Spacer(modifier = Modifier.height(SPACING_MEDIUM))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Checkbox(
|
||||||
|
checked = checkMnt,
|
||||||
|
onCheckedChange = { checkMnt = it }
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(SPACING_SMALL))
|
||||||
|
Text(stringResource(R.string.check_mount_type_overlay))
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(SPACING_MEDIUM))
|
||||||
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = flags,
|
value = flags,
|
||||||
onValueChange = { flags = it },
|
onValueChange = { flags = it },
|
||||||
|
|
@ -372,7 +402,7 @@ fun AddUmountPathDialog(
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
val flagsInt = flags.toIntOrNull() ?: -1
|
val flagsInt = flags.toIntOrNull() ?: -1
|
||||||
onConfirm(path, flagsInt)
|
onConfirm(path, checkMnt, flagsInt)
|
||||||
},
|
},
|
||||||
enabled = path.isNotBlank()
|
enabled = path.isNotBlank()
|
||||||
) {
|
) {
|
||||||
|
|
@ -393,16 +423,18 @@ private fun parseUmountPaths(output: String): List<UmountPathEntry> {
|
||||||
|
|
||||||
return lines.drop(2).mapNotNull { line ->
|
return lines.drop(2).mapNotNull { line ->
|
||||||
val parts = line.trim().split(Regex("\\s+"))
|
val parts = line.trim().split(Regex("\\s+"))
|
||||||
if (parts.size >= 2) {
|
if (parts.size >= 4) {
|
||||||
UmountPathEntry(
|
UmountPathEntry(
|
||||||
path = parts[0],
|
path = parts[0],
|
||||||
flags = parts[1].toIntOrNull() ?: -1
|
checkMnt = parts[1].equals("true", ignoreCase = true),
|
||||||
|
flags = parts[2].toIntOrNull() ?: -1,
|
||||||
|
isDefault = parts[3].equals("Yes", ignoreCase = true)
|
||||||
)
|
)
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Int.toUmountFlagName(context: Context): String {
|
private fun Int.toUmountFlagName(context: android.content.Context): String {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
-1 -> context.getString(R.string.mnt_detach)
|
-1 -> context.getString(R.string.mnt_detach)
|
||||||
else -> this.toString()
|
else -> this.toString()
|
||||||
|
|
|
||||||
|
|
@ -866,9 +866,12 @@ object SuSFSManager {
|
||||||
"CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG" to context.getString(R.string.spoof_cmdline_feature_label),
|
"CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG" to context.getString(R.string.spoof_cmdline_feature_label),
|
||||||
"CONFIG_KSU_SUSFS_OPEN_REDIRECT" to context.getString(R.string.open_redirect_feature_label),
|
"CONFIG_KSU_SUSFS_OPEN_REDIRECT" to context.getString(R.string.open_redirect_feature_label),
|
||||||
"CONFIG_KSU_SUSFS_ENABLE_LOG" to context.getString(R.string.enable_log_feature_label),
|
"CONFIG_KSU_SUSFS_ENABLE_LOG" to context.getString(R.string.enable_log_feature_label),
|
||||||
|
"CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT" to context.getString(R.string.auto_default_mount_feature_label),
|
||||||
|
"CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT" to context.getString(R.string.auto_bind_mount_feature_label),
|
||||||
"CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT" to context.getString(R.string.auto_try_umount_bind_feature_label),
|
"CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT" to context.getString(R.string.auto_try_umount_bind_feature_label),
|
||||||
"CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS" to context.getString(R.string.hide_symbols_feature_label),
|
"CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS" to context.getString(R.string.hide_symbols_feature_label),
|
||||||
"CONFIG_KSU_SUSFS_SUS_KSTAT" to context.getString(R.string.sus_kstat_feature_label),
|
"CONFIG_KSU_SUSFS_SUS_KSTAT" to context.getString(R.string.sus_kstat_feature_label),
|
||||||
|
"CONFIG_KSU_SUSFS_SUS_SU" to context.getString(R.string.sus_su_feature_label)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -896,9 +899,12 @@ object SuSFSManager {
|
||||||
"spoof_cmdline_feature_label" to context.getString(R.string.spoof_cmdline_feature_label),
|
"spoof_cmdline_feature_label" to context.getString(R.string.spoof_cmdline_feature_label),
|
||||||
"open_redirect_feature_label" to context.getString(R.string.open_redirect_feature_label),
|
"open_redirect_feature_label" to context.getString(R.string.open_redirect_feature_label),
|
||||||
"enable_log_feature_label" to context.getString(R.string.enable_log_feature_label),
|
"enable_log_feature_label" to context.getString(R.string.enable_log_feature_label),
|
||||||
|
"auto_default_mount_feature_label" to context.getString(R.string.auto_default_mount_feature_label),
|
||||||
|
"auto_bind_mount_feature_label" to context.getString(R.string.auto_bind_mount_feature_label),
|
||||||
"auto_try_umount_bind_feature_label" to context.getString(R.string.auto_try_umount_bind_feature_label),
|
"auto_try_umount_bind_feature_label" to context.getString(R.string.auto_try_umount_bind_feature_label),
|
||||||
"hide_symbols_feature_label" to context.getString(R.string.hide_symbols_feature_label),
|
"hide_symbols_feature_label" to context.getString(R.string.hide_symbols_feature_label),
|
||||||
"sus_kstat_feature_label" to context.getString(R.string.sus_kstat_feature_label),
|
"sus_kstat_feature_label" to context.getString(R.string.sus_kstat_feature_label),
|
||||||
|
"sus_su_feature_label" to context.getString(R.string.sus_su_feature_label)
|
||||||
)
|
)
|
||||||
|
|
||||||
return defaultFeatures.map { (_, displayName) ->
|
return defaultFeatures.map { (_, displayName) ->
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,572 @@
|
||||||
|
package com.sukisu.ultra.ui.util;
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.text.Collator;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object to convert Chinese character to its corresponding pinyin string. For characters with
|
||||||
|
* multiple possible pinyin string, only one is selected according to collator. Polyphone is not
|
||||||
|
* supported in this implementation. This class is implemented to achieve the best runtime
|
||||||
|
* performance and minimum runtime resources with tolerable sacrifice of accuracy. This
|
||||||
|
* implementation highly depends on zh_CN ICU collation data and must be always synchronized with
|
||||||
|
* ICU.
|
||||||
|
* <p>
|
||||||
|
* Currently this file is aligned to zh.txt in ICU 4.6
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("SizeReplaceableByIsEmpty")
|
||||||
|
public record HanziToPinyin(boolean mHasChinaCollator) {
|
||||||
|
private static final String TAG = "HanziToPinyin";
|
||||||
|
|
||||||
|
// Turn on this flag when we want to check internal data structure.
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unihans array.
|
||||||
|
* <p>
|
||||||
|
* Each unihans is the first one within same pinyin when collator is zh_CN.
|
||||||
|
*/
|
||||||
|
public static final char[] UNIHANS = {
|
||||||
|
'阿', '哎', '安', '肮', '凹', '八',
|
||||||
|
'挀', '扳', '邦', '勹', '陂', '奔',
|
||||||
|
'伻', '屄', '边', '灬', '憋', '汃',
|
||||||
|
'冫', '癶', '峬', '嚓', '偲', '参',
|
||||||
|
'仓', '撡', '冊', '嵾', '曽', '曾',
|
||||||
|
'層', '叉', '芆', '辿', '伥', '抄',
|
||||||
|
'车', '抻', '沈', '沉', '阷', '吃',
|
||||||
|
'充', '抽', '出', '欻', '揣', '巛',
|
||||||
|
'刅', '吹', '旾', '逴', '呲', '匆',
|
||||||
|
'凑', '粗', '汆', '崔', '邨', '搓',
|
||||||
|
'咑', '呆', '丹', '当', '刀', '嘚',
|
||||||
|
'扥', '灯', '氐', '嗲', '甸', '刁',
|
||||||
|
'爹', '丁', '丟', '东', '吺', '厾',
|
||||||
|
'耑', '襨', '吨', '多', '妸', '诶',
|
||||||
|
'奀', '鞥', '儿', '发', '帆', '匚',
|
||||||
|
'飞', '分', '丰', '覅', '仏', '紑',
|
||||||
|
'伕', '旮', '侅', '甘', '冈', '皋',
|
||||||
|
'戈', '给', '根', '刯', '工', '勾',
|
||||||
|
'估', '瓜', '乖', '关', '光', '归',
|
||||||
|
'丨', '呙', '哈', '咍', '佄', '夯',
|
||||||
|
'茠', '诃', '黒', '拫', '亨', '噷',
|
||||||
|
'叿', '齁', '乯', '花', '怀', '犿',
|
||||||
|
'巟', '灰', '昏', '吙', '丌', '加',
|
||||||
|
'戋', '江', '艽', '阶', '巾', '坕',
|
||||||
|
'冂', '丩', '凥', '姢', '噘', '军',
|
||||||
|
'咔', '开', '刊', '忼', '尻', '匼',
|
||||||
|
'肎', '劥', '空', '抠', '扝', '夸',
|
||||||
|
'蒯', '宽', '匡', '亏', '坤', '扩',
|
||||||
|
'垃', '来', '兰', '啷', '捞', '肋',
|
||||||
|
'勒', '崚', '刕', '俩', '奁', '良',
|
||||||
|
'撩', '列', '拎', '刢', '溜', '囖',
|
||||||
|
'龙', '瞜', '噜', '娈', '畧', '抡',
|
||||||
|
'罗', '呣', '妈', '埋', '嫚', '牤',
|
||||||
|
'猫', '么', '呅', '门', '甿', '咪',
|
||||||
|
'宀', '喵', '乜', '民', '名', '谬',
|
||||||
|
'摸', '哞', '毪', '嗯', '拏', '腉',
|
||||||
|
'囡', '囔', '孬', '疒', '娞', '恁',
|
||||||
|
'能', '妮', '拈', '嬢', '鸟', '捏',
|
||||||
|
'囜', '宁', '妞', '农', '羺', '奴',
|
||||||
|
'奻', '疟', '黁', '郍', '喔', '讴',
|
||||||
|
'妑', '拍', '眅', '乓', '抛', '呸',
|
||||||
|
'喷', '匉', '丕', '囨', '剽', '氕',
|
||||||
|
'姘', '乒', '钋', '剖', '仆', '七',
|
||||||
|
'掐', '千', '呛', '悄', '癿', '亲',
|
||||||
|
'狅', '芎', '丘', '区', '峑', '缺',
|
||||||
|
'夋', '呥', '穣', '娆', '惹', '人',
|
||||||
|
'扔', '日', '茸', '厹', '邚', '挼',
|
||||||
|
'堧', '婑', '瞤', '捼', '仨', '毢',
|
||||||
|
'三', '桒', '掻', '閪', '森', '僧',
|
||||||
|
'杀', '筛', '山', '伤', '弰', '奢',
|
||||||
|
'申', '莘', '敒', '升', '尸', '収',
|
||||||
|
'书', '刷', '衰', '闩', '双', '谁',
|
||||||
|
'吮', '说', '厶', '忪', '捜', '苏',
|
||||||
|
'狻', '夊', '孙', '唆', '他', '囼',
|
||||||
|
'坍', '汤', '夲', '忑', '熥', '剔',
|
||||||
|
'天', '旫', '帖', '厅', '囲', '偷',
|
||||||
|
'凸', '湍', '推', '吞', '乇', '穵',
|
||||||
|
'歪', '弯', '尣', '危', '昷', '翁',
|
||||||
|
'挝', '乌', '夕', '虲', '仚', '乡',
|
||||||
|
'灱', '些', '心', '星', '凶', '休',
|
||||||
|
'吁', '吅', '削', '坃', '丫', '恹',
|
||||||
|
'央', '幺', '倻', '一', '囙', '应',
|
||||||
|
'哟', '佣', '优', '扜', '囦', '曰',
|
||||||
|
'晕', '筠', '筼', '帀', '災', '兂',
|
||||||
|
'匨', '傮', '则', '贼', '怎', '増',
|
||||||
|
'扎', '捚', '沾', '张', '长', '長',
|
||||||
|
'佋', '蜇', '贞', '争', '之', '峙',
|
||||||
|
'庢', '中', '州', '朱', '抓', '拽',
|
||||||
|
'专', '妆', '隹', '宒', '卓', '乲',
|
||||||
|
'宗', '邹', '租', '钻', '厜', '尊',
|
||||||
|
'昨', '兙', '鿃', '鿄'};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pinyin array.
|
||||||
|
* <p>
|
||||||
|
* Each pinyin is corresponding to unihans of same
|
||||||
|
* offset in the unihans array.
|
||||||
|
*/
|
||||||
|
public static final byte[][] PINYINS = {
|
||||||
|
{65, 0, 0, 0, 0, 0}, {65, 73, 0, 0, 0, 0},
|
||||||
|
{65, 78, 0, 0, 0, 0}, {65, 78, 71, 0, 0, 0},
|
||||||
|
{65, 79, 0, 0, 0, 0}, {66, 65, 0, 0, 0, 0},
|
||||||
|
{66, 65, 73, 0, 0, 0}, {66, 65, 78, 0, 0, 0},
|
||||||
|
{66, 65, 78, 71, 0, 0}, {66, 65, 79, 0, 0, 0},
|
||||||
|
{66, 69, 73, 0, 0, 0}, {66, 69, 78, 0, 0, 0},
|
||||||
|
{66, 69, 78, 71, 0, 0}, {66, 73, 0, 0, 0, 0},
|
||||||
|
{66, 73, 65, 78, 0, 0}, {66, 73, 65, 79, 0, 0},
|
||||||
|
{66, 73, 69, 0, 0, 0}, {66, 73, 78, 0, 0, 0},
|
||||||
|
{66, 73, 78, 71, 0, 0}, {66, 79, 0, 0, 0, 0},
|
||||||
|
{66, 85, 0, 0, 0, 0}, {67, 65, 0, 0, 0, 0},
|
||||||
|
{67, 65, 73, 0, 0, 0}, {67, 65, 78, 0, 0, 0},
|
||||||
|
{67, 65, 78, 71, 0, 0}, {67, 65, 79, 0, 0, 0},
|
||||||
|
{67, 69, 0, 0, 0, 0}, {67, 69, 78, 0, 0, 0},
|
||||||
|
{67, 69, 78, 71, 0, 0}, {90, 69, 78, 71, 0, 0},
|
||||||
|
{67, 69, 78, 71, 0, 0}, {67, 72, 65, 0, 0, 0},
|
||||||
|
{67, 72, 65, 73, 0, 0}, {67, 72, 65, 78, 0, 0},
|
||||||
|
{67, 72, 65, 78, 71, 0}, {67, 72, 65, 79, 0, 0},
|
||||||
|
{67, 72, 69, 0, 0, 0}, {67, 72, 69, 78, 0, 0},
|
||||||
|
{83, 72, 69, 78, 0, 0}, {67, 72, 69, 78, 0, 0},
|
||||||
|
{67, 72, 69, 78, 71, 0}, {67, 72, 73, 0, 0, 0},
|
||||||
|
{67, 72, 79, 78, 71, 0}, {67, 72, 79, 85, 0, 0},
|
||||||
|
{67, 72, 85, 0, 0, 0}, {67, 72, 85, 65, 0, 0},
|
||||||
|
{67, 72, 85, 65, 73, 0}, {67, 72, 85, 65, 78, 0},
|
||||||
|
{67, 72, 85, 65, 78, 71}, {67, 72, 85, 73, 0, 0},
|
||||||
|
{67, 72, 85, 78, 0, 0}, {67, 72, 85, 79, 0, 0},
|
||||||
|
{67, 73, 0, 0, 0, 0}, {67, 79, 78, 71, 0, 0},
|
||||||
|
{67, 79, 85, 0, 0, 0}, {67, 85, 0, 0, 0, 0},
|
||||||
|
{67, 85, 65, 78, 0, 0}, {67, 85, 73, 0, 0, 0},
|
||||||
|
{67, 85, 78, 0, 0, 0}, {67, 85, 79, 0, 0, 0},
|
||||||
|
{68, 65, 0, 0, 0, 0}, {68, 65, 73, 0, 0, 0},
|
||||||
|
{68, 65, 78, 0, 0, 0}, {68, 65, 78, 71, 0, 0},
|
||||||
|
{68, 65, 79, 0, 0, 0}, {68, 69, 0, 0, 0, 0},
|
||||||
|
{68, 69, 78, 0, 0, 0}, {68, 69, 78, 71, 0, 0},
|
||||||
|
{68, 73, 0, 0, 0, 0}, {68, 73, 65, 0, 0, 0},
|
||||||
|
{68, 73, 65, 78, 0, 0}, {68, 73, 65, 79, 0, 0},
|
||||||
|
{68, 73, 69, 0, 0, 0}, {68, 73, 78, 71, 0, 0},
|
||||||
|
{68, 73, 85, 0, 0, 0}, {68, 79, 78, 71, 0, 0},
|
||||||
|
{68, 79, 85, 0, 0, 0}, {68, 85, 0, 0, 0, 0},
|
||||||
|
{68, 85, 65, 78, 0, 0}, {68, 85, 73, 0, 0, 0},
|
||||||
|
{68, 85, 78, 0, 0, 0}, {68, 85, 79, 0, 0, 0},
|
||||||
|
{69, 0, 0, 0, 0, 0}, {69, 73, 0, 0, 0, 0},
|
||||||
|
{69, 78, 0, 0, 0, 0}, {69, 78, 71, 0, 0, 0},
|
||||||
|
{69, 82, 0, 0, 0, 0}, {70, 65, 0, 0, 0, 0},
|
||||||
|
{70, 65, 78, 0, 0, 0}, {70, 65, 78, 71, 0, 0},
|
||||||
|
{70, 69, 73, 0, 0, 0}, {70, 69, 78, 0, 0, 0},
|
||||||
|
{70, 69, 78, 71, 0, 0}, {70, 73, 65, 79, 0, 0},
|
||||||
|
{70, 79, 0, 0, 0, 0}, {70, 79, 85, 0, 0, 0},
|
||||||
|
{70, 85, 0, 0, 0, 0}, {71, 65, 0, 0, 0, 0},
|
||||||
|
{71, 65, 73, 0, 0, 0}, {71, 65, 78, 0, 0, 0},
|
||||||
|
{71, 65, 78, 71, 0, 0}, {71, 65, 79, 0, 0, 0},
|
||||||
|
{71, 69, 0, 0, 0, 0}, {71, 69, 73, 0, 0, 0},
|
||||||
|
{71, 69, 78, 0, 0, 0}, {71, 69, 78, 71, 0, 0},
|
||||||
|
{71, 79, 78, 71, 0, 0}, {71, 79, 85, 0, 0, 0},
|
||||||
|
{71, 85, 0, 0, 0, 0}, {71, 85, 65, 0, 0, 0},
|
||||||
|
{71, 85, 65, 73, 0, 0}, {71, 85, 65, 78, 0, 0},
|
||||||
|
{71, 85, 65, 78, 71, 0}, {71, 85, 73, 0, 0, 0},
|
||||||
|
{71, 85, 78, 0, 0, 0}, {71, 85, 79, 0, 0, 0},
|
||||||
|
{72, 65, 0, 0, 0, 0}, {72, 65, 73, 0, 0, 0},
|
||||||
|
{72, 65, 78, 0, 0, 0}, {72, 65, 78, 71, 0, 0},
|
||||||
|
{72, 65, 79, 0, 0, 0}, {72, 69, 0, 0, 0, 0},
|
||||||
|
{72, 69, 73, 0, 0, 0}, {72, 69, 78, 0, 0, 0},
|
||||||
|
{72, 69, 78, 71, 0, 0}, {72, 77, 0, 0, 0, 0},
|
||||||
|
{72, 79, 78, 71, 0, 0}, {72, 79, 85, 0, 0, 0},
|
||||||
|
{72, 85, 0, 0, 0, 0}, {72, 85, 65, 0, 0, 0},
|
||||||
|
{72, 85, 65, 73, 0, 0}, {72, 85, 65, 78, 0, 0},
|
||||||
|
{72, 85, 65, 78, 71, 0}, {72, 85, 73, 0, 0, 0},
|
||||||
|
{72, 85, 78, 0, 0, 0}, {72, 85, 79, 0, 0, 0},
|
||||||
|
{74, 73, 0, 0, 0, 0}, {74, 73, 65, 0, 0, 0},
|
||||||
|
{74, 73, 65, 78, 0, 0}, {74, 73, 65, 78, 71, 0},
|
||||||
|
{74, 73, 65, 79, 0, 0}, {74, 73, 69, 0, 0, 0},
|
||||||
|
{74, 73, 78, 0, 0, 0}, {74, 73, 78, 71, 0, 0},
|
||||||
|
{74, 73, 79, 78, 71, 0}, {74, 73, 85, 0, 0, 0},
|
||||||
|
{74, 85, 0, 0, 0, 0}, {74, 85, 65, 78, 0, 0},
|
||||||
|
{74, 85, 69, 0, 0, 0}, {74, 85, 78, 0, 0, 0},
|
||||||
|
{75, 65, 0, 0, 0, 0}, {75, 65, 73, 0, 0, 0},
|
||||||
|
{75, 65, 78, 0, 0, 0}, {75, 65, 78, 71, 0, 0},
|
||||||
|
{75, 65, 79, 0, 0, 0}, {75, 69, 0, 0, 0, 0},
|
||||||
|
{75, 69, 78, 0, 0, 0}, {75, 69, 78, 71, 0, 0},
|
||||||
|
{75, 79, 78, 71, 0, 0}, {75, 79, 85, 0, 0, 0},
|
||||||
|
{75, 85, 0, 0, 0, 0}, {75, 85, 65, 0, 0, 0},
|
||||||
|
{75, 85, 65, 73, 0, 0}, {75, 85, 65, 78, 0, 0},
|
||||||
|
{75, 85, 65, 78, 71, 0}, {75, 85, 73, 0, 0, 0},
|
||||||
|
{75, 85, 78, 0, 0, 0}, {75, 85, 79, 0, 0, 0},
|
||||||
|
{76, 65, 0, 0, 0, 0}, {76, 65, 73, 0, 0, 0},
|
||||||
|
{76, 65, 78, 0, 0, 0}, {76, 65, 78, 71, 0, 0},
|
||||||
|
{76, 65, 79, 0, 0, 0}, {76, 69, 0, 0, 0, 0},
|
||||||
|
{76, 69, 73, 0, 0, 0}, {76, 69, 78, 71, 0, 0},
|
||||||
|
{76, 73, 0, 0, 0, 0}, {76, 73, 65, 0, 0, 0},
|
||||||
|
{76, 73, 65, 78, 0, 0}, {76, 73, 65, 78, 71, 0},
|
||||||
|
{76, 73, 65, 79, 0, 0}, {76, 73, 69, 0, 0, 0},
|
||||||
|
{76, 73, 78, 0, 0, 0}, {76, 73, 78, 71, 0, 0},
|
||||||
|
{76, 73, 85, 0, 0, 0}, {76, 79, 0, 0, 0, 0},
|
||||||
|
{76, 79, 78, 71, 0, 0}, {76, 79, 85, 0, 0, 0},
|
||||||
|
{76, 85, 0, 0, 0, 0}, {76, 85, 65, 78, 0, 0},
|
||||||
|
{76, 85, 69, 0, 0, 0}, {76, 85, 78, 0, 0, 0},
|
||||||
|
{76, 85, 79, 0, 0, 0}, {77, 0, 0, 0, 0, 0},
|
||||||
|
{77, 65, 0, 0, 0, 0}, {77, 65, 73, 0, 0, 0},
|
||||||
|
{77, 65, 78, 0, 0, 0}, {77, 65, 78, 71, 0, 0},
|
||||||
|
{77, 65, 79, 0, 0, 0}, {77, 69, 0, 0, 0, 0},
|
||||||
|
{77, 69, 73, 0, 0, 0}, {77, 69, 78, 0, 0, 0},
|
||||||
|
{77, 69, 78, 71, 0, 0}, {77, 73, 0, 0, 0, 0},
|
||||||
|
{77, 73, 65, 78, 0, 0}, {77, 73, 65, 79, 0, 0},
|
||||||
|
{77, 73, 69, 0, 0, 0}, {77, 73, 78, 0, 0, 0},
|
||||||
|
{77, 73, 78, 71, 0, 0}, {77, 73, 85, 0, 0, 0},
|
||||||
|
{77, 79, 0, 0, 0, 0}, {77, 79, 85, 0, 0, 0},
|
||||||
|
{77, 85, 0, 0, 0, 0}, {78, 0, 0, 0, 0, 0},
|
||||||
|
{78, 65, 0, 0, 0, 0}, {78, 65, 73, 0, 0, 0},
|
||||||
|
{78, 65, 78, 0, 0, 0}, {78, 65, 78, 71, 0, 0},
|
||||||
|
{78, 65, 79, 0, 0, 0}, {78, 69, 0, 0, 0, 0},
|
||||||
|
{78, 69, 73, 0, 0, 0}, {78, 69, 78, 0, 0, 0},
|
||||||
|
{78, 69, 78, 71, 0, 0}, {78, 73, 0, 0, 0, 0},
|
||||||
|
{78, 73, 65, 78, 0, 0}, {78, 73, 65, 78, 71, 0},
|
||||||
|
{78, 73, 65, 79, 0, 0}, {78, 73, 69, 0, 0, 0},
|
||||||
|
{78, 73, 78, 0, 0, 0}, {78, 73, 78, 71, 0, 0},
|
||||||
|
{78, 73, 85, 0, 0, 0}, {78, 79, 78, 71, 0, 0},
|
||||||
|
{78, 79, 85, 0, 0, 0}, {78, 85, 0, 0, 0, 0},
|
||||||
|
{78, 85, 65, 78, 0, 0}, {78, 85, 69, 0, 0, 0},
|
||||||
|
{78, 85, 78, 0, 0, 0}, {78, 85, 79, 0, 0, 0},
|
||||||
|
{79, 0, 0, 0, 0, 0}, {79, 85, 0, 0, 0, 0},
|
||||||
|
{80, 65, 0, 0, 0, 0}, {80, 65, 73, 0, 0, 0},
|
||||||
|
{80, 65, 78, 0, 0, 0}, {80, 65, 78, 71, 0, 0},
|
||||||
|
{80, 65, 79, 0, 0, 0}, {80, 69, 73, 0, 0, 0},
|
||||||
|
{80, 69, 78, 0, 0, 0}, {80, 69, 78, 71, 0, 0},
|
||||||
|
{80, 73, 0, 0, 0, 0}, {80, 73, 65, 78, 0, 0},
|
||||||
|
{80, 73, 65, 79, 0, 0}, {80, 73, 69, 0, 0, 0},
|
||||||
|
{80, 73, 78, 0, 0, 0}, {80, 73, 78, 71, 0, 0},
|
||||||
|
{80, 79, 0, 0, 0, 0}, {80, 79, 85, 0, 0, 0},
|
||||||
|
{80, 85, 0, 0, 0, 0}, {81, 73, 0, 0, 0, 0},
|
||||||
|
{81, 73, 65, 0, 0, 0}, {81, 73, 65, 78, 0, 0},
|
||||||
|
{81, 73, 65, 78, 71, 0}, {81, 73, 65, 79, 0, 0},
|
||||||
|
{81, 73, 69, 0, 0, 0}, {81, 73, 78, 0, 0, 0},
|
||||||
|
{81, 73, 78, 71, 0, 0}, {81, 73, 79, 78, 71, 0},
|
||||||
|
{81, 73, 85, 0, 0, 0}, {81, 85, 0, 0, 0, 0},
|
||||||
|
{81, 85, 65, 78, 0, 0}, {81, 85, 69, 0, 0, 0},
|
||||||
|
{81, 85, 78, 0, 0, 0}, {82, 65, 78, 0, 0, 0},
|
||||||
|
{82, 65, 78, 71, 0, 0}, {82, 65, 79, 0, 0, 0},
|
||||||
|
{82, 69, 0, 0, 0, 0}, {82, 69, 78, 0, 0, 0},
|
||||||
|
{82, 69, 78, 71, 0, 0}, {82, 73, 0, 0, 0, 0},
|
||||||
|
{82, 79, 78, 71, 0, 0}, {82, 79, 85, 0, 0, 0},
|
||||||
|
{82, 85, 0, 0, 0, 0}, {82, 85, 65, 0, 0, 0},
|
||||||
|
{82, 85, 65, 78, 0, 0}, {82, 85, 73, 0, 0, 0},
|
||||||
|
{82, 85, 78, 0, 0, 0}, {82, 85, 79, 0, 0, 0},
|
||||||
|
{83, 65, 0, 0, 0, 0}, {83, 65, 73, 0, 0, 0},
|
||||||
|
{83, 65, 78, 0, 0, 0}, {83, 65, 78, 71, 0, 0},
|
||||||
|
{83, 65, 79, 0, 0, 0}, {83, 69, 0, 0, 0, 0},
|
||||||
|
{83, 69, 78, 0, 0, 0}, {83, 69, 78, 71, 0, 0},
|
||||||
|
{83, 72, 65, 0, 0, 0}, {83, 72, 65, 73, 0, 0},
|
||||||
|
{83, 72, 65, 78, 0, 0}, {83, 72, 65, 78, 71, 0},
|
||||||
|
{83, 72, 65, 79, 0, 0}, {83, 72, 69, 0, 0, 0},
|
||||||
|
{83, 72, 69, 78, 0, 0}, {88, 73, 78, 0, 0, 0},
|
||||||
|
{83, 72, 69, 78, 0, 0}, {83, 72, 69, 78, 71, 0},
|
||||||
|
{83, 72, 73, 0, 0, 0}, {83, 72, 79, 85, 0, 0},
|
||||||
|
{83, 72, 85, 0, 0, 0}, {83, 72, 85, 65, 0, 0},
|
||||||
|
{83, 72, 85, 65, 73, 0}, {83, 72, 85, 65, 78, 0},
|
||||||
|
{83, 72, 85, 65, 78, 71}, {83, 72, 85, 73, 0, 0},
|
||||||
|
{83, 72, 85, 78, 0, 0}, {83, 72, 85, 79, 0, 0},
|
||||||
|
{83, 73, 0, 0, 0, 0}, {83, 79, 78, 71, 0, 0},
|
||||||
|
{83, 79, 85, 0, 0, 0}, {83, 85, 0, 0, 0, 0},
|
||||||
|
{83, 85, 65, 78, 0, 0}, {83, 85, 73, 0, 0, 0},
|
||||||
|
{83, 85, 78, 0, 0, 0}, {83, 85, 79, 0, 0, 0},
|
||||||
|
{84, 65, 0, 0, 0, 0}, {84, 65, 73, 0, 0, 0},
|
||||||
|
{84, 65, 78, 0, 0, 0}, {84, 65, 78, 71, 0, 0},
|
||||||
|
{84, 65, 79, 0, 0, 0}, {84, 69, 0, 0, 0, 0},
|
||||||
|
{84, 69, 78, 71, 0, 0}, {84, 73, 0, 0, 0, 0},
|
||||||
|
{84, 73, 65, 78, 0, 0}, {84, 73, 65, 79, 0, 0},
|
||||||
|
{84, 73, 69, 0, 0, 0}, {84, 73, 78, 71, 0, 0},
|
||||||
|
{84, 79, 78, 71, 0, 0}, {84, 79, 85, 0, 0, 0},
|
||||||
|
{84, 85, 0, 0, 0, 0}, {84, 85, 65, 78, 0, 0},
|
||||||
|
{84, 85, 73, 0, 0, 0}, {84, 85, 78, 0, 0, 0},
|
||||||
|
{84, 85, 79, 0, 0, 0}, {87, 65, 0, 0, 0, 0},
|
||||||
|
{87, 65, 73, 0, 0, 0}, {87, 65, 78, 0, 0, 0},
|
||||||
|
{87, 65, 78, 71, 0, 0}, {87, 69, 73, 0, 0, 0},
|
||||||
|
{87, 69, 78, 0, 0, 0}, {87, 69, 78, 71, 0, 0},
|
||||||
|
{87, 79, 0, 0, 0, 0}, {87, 85, 0, 0, 0, 0},
|
||||||
|
{88, 73, 0, 0, 0, 0}, {88, 73, 65, 0, 0, 0},
|
||||||
|
{88, 73, 65, 78, 0, 0}, {88, 73, 65, 78, 71, 0},
|
||||||
|
{88, 73, 65, 79, 0, 0}, {88, 73, 69, 0, 0, 0},
|
||||||
|
{88, 73, 78, 0, 0, 0}, {88, 73, 78, 71, 0, 0},
|
||||||
|
{88, 73, 79, 78, 71, 0}, {88, 73, 85, 0, 0, 0},
|
||||||
|
{88, 85, 0, 0, 0, 0}, {88, 85, 65, 78, 0, 0},
|
||||||
|
{88, 85, 69, 0, 0, 0}, {88, 85, 78, 0, 0, 0},
|
||||||
|
{89, 65, 0, 0, 0, 0}, {89, 65, 78, 0, 0, 0},
|
||||||
|
{89, 65, 78, 71, 0, 0}, {89, 65, 79, 0, 0, 0},
|
||||||
|
{89, 69, 0, 0, 0, 0}, {89, 73, 0, 0, 0, 0},
|
||||||
|
{89, 73, 78, 0, 0, 0}, {89, 73, 78, 71, 0, 0},
|
||||||
|
{89, 79, 0, 0, 0, 0}, {89, 79, 78, 71, 0, 0},
|
||||||
|
{89, 79, 85, 0, 0, 0}, {89, 85, 0, 0, 0, 0},
|
||||||
|
{89, 85, 65, 78, 0, 0}, {89, 85, 69, 0, 0, 0},
|
||||||
|
{89, 85, 78, 0, 0, 0}, {74, 85, 78, 0, 0, 0},
|
||||||
|
{89, 85, 78, 0, 0, 0}, {90, 65, 0, 0, 0, 0},
|
||||||
|
{90, 65, 73, 0, 0, 0}, {90, 65, 78, 0, 0, 0},
|
||||||
|
{90, 65, 78, 71, 0, 0}, {90, 65, 79, 0, 0, 0},
|
||||||
|
{90, 69, 0, 0, 0, 0}, {90, 69, 73, 0, 0, 0},
|
||||||
|
{90, 69, 78, 0, 0, 0}, {90, 69, 78, 71, 0, 0},
|
||||||
|
{90, 72, 65, 0, 0, 0}, {90, 72, 65, 73, 0, 0},
|
||||||
|
{90, 72, 65, 78, 0, 0}, {90, 72, 65, 78, 71, 0},
|
||||||
|
{67, 72, 65, 78, 71, 0}, {90, 72, 65, 78, 71, 0},
|
||||||
|
{90, 72, 65, 79, 0, 0}, {90, 72, 69, 0, 0, 0},
|
||||||
|
{90, 72, 69, 78, 0, 0}, {90, 72, 69, 78, 71, 0},
|
||||||
|
{90, 72, 73, 0, 0, 0}, {83, 72, 73, 0, 0, 0},
|
||||||
|
{90, 72, 73, 0, 0, 0}, {90, 72, 79, 78, 71, 0},
|
||||||
|
{90, 72, 79, 85, 0, 0}, {90, 72, 85, 0, 0, 0},
|
||||||
|
{90, 72, 85, 65, 0, 0}, {90, 72, 85, 65, 73, 0},
|
||||||
|
{90, 72, 85, 65, 78, 0}, {90, 72, 85, 65, 78, 71},
|
||||||
|
{90, 72, 85, 73, 0, 0}, {90, 72, 85, 78, 0, 0},
|
||||||
|
{90, 72, 85, 79, 0, 0}, {90, 73, 0, 0, 0, 0},
|
||||||
|
{90, 79, 78, 71, 0, 0}, {90, 79, 85, 0, 0, 0},
|
||||||
|
{90, 85, 0, 0, 0, 0}, {90, 85, 65, 78, 0, 0},
|
||||||
|
{90, 85, 73, 0, 0, 0}, {90, 85, 78, 0, 0, 0},
|
||||||
|
{90, 85, 79, 0, 0, 0}, {0, 0, 0, 0, 0, 0},
|
||||||
|
{83, 72, 65, 78, 0, 0}, {0, 0, 0, 0, 0, 0}};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First and last Chinese character with known Pinyin according to zh collation
|
||||||
|
*/
|
||||||
|
private static final String FIRST_PINYIN_UNIHAN = "阿";
|
||||||
|
private static final String LAST_PINYIN_UNIHAN = "鿿";
|
||||||
|
|
||||||
|
private static final Collator COLLATOR = Collator.getInstance(Locale.CHINA);
|
||||||
|
|
||||||
|
private static HanziToPinyin sInstance;
|
||||||
|
|
||||||
|
public static class Token {
|
||||||
|
/**
|
||||||
|
* Separator between target string for each source char
|
||||||
|
*/
|
||||||
|
public static final String SEPARATOR = " ";
|
||||||
|
|
||||||
|
public static final int LATIN = 1;
|
||||||
|
public static final int PINYIN = 2;
|
||||||
|
public static final int UNKNOWN = 3;
|
||||||
|
|
||||||
|
public Token() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Token(int type, String source, String target) {
|
||||||
|
this.type = type;
|
||||||
|
this.source = source;
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of this token, ASCII, PINYIN or UNKNOWN.
|
||||||
|
*/
|
||||||
|
public int type;
|
||||||
|
/**
|
||||||
|
* Original string before translation.
|
||||||
|
*/
|
||||||
|
public String source;
|
||||||
|
/**
|
||||||
|
* Translated string of source. For Han, target is corresponding Pinyin. Otherwise target is
|
||||||
|
* original string in source.
|
||||||
|
*/
|
||||||
|
public String target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HanziToPinyin getInstance() {
|
||||||
|
synchronized (HanziToPinyin.class) {
|
||||||
|
if (sInstance != null) {
|
||||||
|
return sInstance;
|
||||||
|
}
|
||||||
|
// Check if zh_CN collation data is available
|
||||||
|
final Locale[] locale = Collator.getAvailableLocales();
|
||||||
|
for (Locale value : locale) {
|
||||||
|
if (value.equals(Locale.CHINA) || value.getLanguage().contains("zh")) {
|
||||||
|
// Do self validation just once.
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "Self validation. Result: " + doSelfValidation());
|
||||||
|
}
|
||||||
|
sInstance = new HanziToPinyin(true);
|
||||||
|
return sInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sInstance == null) {//这个判断是用于处理国产ROM的兼容性问题
|
||||||
|
if (Locale.CHINA.equals(Locale.getDefault())) {
|
||||||
|
sInstance = new HanziToPinyin(true);
|
||||||
|
return sInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.w(TAG, "There is no Chinese collator, HanziToPinyin is disabled");
|
||||||
|
sInstance = new HanziToPinyin(false);
|
||||||
|
return sInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate if our internal table has some wrong value.
|
||||||
|
*
|
||||||
|
* @return true when the table looks correct.
|
||||||
|
*/
|
||||||
|
private static boolean doSelfValidation() {
|
||||||
|
char lastChar = UNIHANS[0];
|
||||||
|
String lastString = Character.toString(lastChar);
|
||||||
|
for (char c : UNIHANS) {
|
||||||
|
if (lastChar == c) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final String curString = Character.toString(c);
|
||||||
|
int cmp = COLLATOR.compare(lastString, curString);
|
||||||
|
if (cmp >= 0) {
|
||||||
|
Log.e(TAG, "Internal error in Unihan table. " + "The last string \"" + lastString
|
||||||
|
+ "\" is greater than current string \"" + curString + "\".");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
lastString = curString;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token getToken(char character) {
|
||||||
|
Token token = new Token();
|
||||||
|
final String letter = Character.toString(character);
|
||||||
|
token.source = letter;
|
||||||
|
int offset = -1;
|
||||||
|
int cmp;
|
||||||
|
if (character < 256) {
|
||||||
|
token.type = Token.LATIN;
|
||||||
|
token.target = letter;
|
||||||
|
return token;
|
||||||
|
} else {
|
||||||
|
cmp = COLLATOR.compare(letter, FIRST_PINYIN_UNIHAN);
|
||||||
|
if (cmp < 0) {
|
||||||
|
token.type = Token.UNKNOWN;
|
||||||
|
token.target = letter;
|
||||||
|
return token;
|
||||||
|
} else if (cmp == 0) {
|
||||||
|
token.type = Token.PINYIN;
|
||||||
|
offset = 0;
|
||||||
|
} else {
|
||||||
|
cmp = COLLATOR.compare(letter, LAST_PINYIN_UNIHAN);
|
||||||
|
if (cmp > 0) {
|
||||||
|
token.type = Token.UNKNOWN;
|
||||||
|
token.target = letter;
|
||||||
|
return token;
|
||||||
|
} else if (cmp == 0) {
|
||||||
|
token.type = Token.PINYIN;
|
||||||
|
offset = UNIHANS.length - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token.type = Token.PINYIN;
|
||||||
|
if (offset < 0) {
|
||||||
|
int begin = 0;
|
||||||
|
int end = UNIHANS.length - 1;
|
||||||
|
while (begin <= end) {
|
||||||
|
offset = (begin + end) / 2;
|
||||||
|
final String unihan = Character.toString(UNIHANS[offset]);
|
||||||
|
cmp = COLLATOR.compare(letter, unihan);
|
||||||
|
if (cmp == 0) {
|
||||||
|
break;
|
||||||
|
} else if (cmp > 0) {
|
||||||
|
begin = offset + 1;
|
||||||
|
} else {
|
||||||
|
end = offset - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cmp < 0) {
|
||||||
|
offset--;
|
||||||
|
}
|
||||||
|
StringBuilder pinyin = new StringBuilder();
|
||||||
|
for (int j = 0; j < PINYINS[offset].length && PINYINS[offset][j] != 0; j++) {
|
||||||
|
pinyin.append((char) PINYINS[offset][j]);
|
||||||
|
}
|
||||||
|
token.target = pinyin.toString();
|
||||||
|
if (TextUtils.isEmpty(token.target)) {
|
||||||
|
token.type = Token.UNKNOWN;
|
||||||
|
token.target = token.source;
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the input to a array of tokens. The sequence of ASCII or Unknown characters without
|
||||||
|
* space will be put into a Token, One Hanzi character which has pinyin will be treated as a
|
||||||
|
* Token. If these is no China collator, the empty token array is returned.
|
||||||
|
*/
|
||||||
|
public ArrayList<Token> get(final String input) {
|
||||||
|
ArrayList<Token> tokens = new ArrayList<>();
|
||||||
|
if (!mHasChinaCollator || TextUtils.isEmpty(input)) {
|
||||||
|
// return empty tokens.
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
final int inputLength = input.length();
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
int tokenType = Token.LATIN;
|
||||||
|
// Go through the input, create a new token when
|
||||||
|
// a. Token type changed
|
||||||
|
// b. Get the Pinyin of current charater.
|
||||||
|
// c. current character is space.
|
||||||
|
for (int i = 0; i < inputLength; i++) {
|
||||||
|
final char character = input.charAt(i);
|
||||||
|
if (character == ' ') {
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
addToken(sb, tokens, tokenType);
|
||||||
|
}
|
||||||
|
} else if (character < 256) {
|
||||||
|
if (tokenType != Token.LATIN && sb.length() > 0) {
|
||||||
|
addToken(sb, tokens, tokenType);
|
||||||
|
}
|
||||||
|
tokenType = Token.LATIN;
|
||||||
|
sb.append(character);
|
||||||
|
} else {
|
||||||
|
Token t = getToken(character);
|
||||||
|
if (t.type == Token.PINYIN) {
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
addToken(sb, tokens, tokenType);
|
||||||
|
}
|
||||||
|
tokens.add(t);
|
||||||
|
tokenType = Token.PINYIN;
|
||||||
|
} else {
|
||||||
|
if (tokenType != t.type && sb.length() > 0) {
|
||||||
|
addToken(sb, tokens, tokenType);
|
||||||
|
}
|
||||||
|
tokenType = t.type;
|
||||||
|
sb.append(character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
addToken(sb, tokens, tokenType);
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addToken(
|
||||||
|
final StringBuilder sb, final ArrayList<Token> tokens, final int tokenType) {
|
||||||
|
String str = sb.toString();
|
||||||
|
tokens.add(new Token(tokenType, str, str));
|
||||||
|
sb.setLength(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toPinyinString(String string) {
|
||||||
|
if (string == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
ArrayList<Token> tokens = get(string);
|
||||||
|
for (Token token : tokens) {
|
||||||
|
sb.append(token.target);
|
||||||
|
}
|
||||||
|
return sb.toString().toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,522 +0,0 @@
|
||||||
package com.sukisu.ultra.ui.util
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2009 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import android.text.TextUtils
|
|
||||||
import android.util.Log
|
|
||||||
import java.text.Collator
|
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
class HanziToPinyin private constructor(val hasChinaCollator: Boolean) {
|
|
||||||
|
|
||||||
class Token(
|
|
||||||
var type: Int = 0,
|
|
||||||
var source: String = "",
|
|
||||||
var target: String = ""
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
const val LATIN = 1
|
|
||||||
const val PINYIN = 2
|
|
||||||
const val UNKNOWN = 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getToken(character: Char): Token {
|
|
||||||
val token = Token()
|
|
||||||
val letter = character.toString()
|
|
||||||
token.source = letter
|
|
||||||
var offset = -1
|
|
||||||
var cmp: Int
|
|
||||||
|
|
||||||
if (character < 256.toChar()) {
|
|
||||||
token.type = Token.LATIN
|
|
||||||
token.target = letter
|
|
||||||
return token
|
|
||||||
} else {
|
|
||||||
cmp = COLLATOR.compare(letter, FIRST_PINYIN_UNIHAN)
|
|
||||||
if (cmp < 0) {
|
|
||||||
token.type = Token.UNKNOWN
|
|
||||||
token.target = letter
|
|
||||||
return token
|
|
||||||
} else if (cmp == 0) {
|
|
||||||
token.type = Token.PINYIN
|
|
||||||
offset = 0
|
|
||||||
} else {
|
|
||||||
cmp = COLLATOR.compare(letter, LAST_PINYIN_UNIHAN)
|
|
||||||
if (cmp > 0) {
|
|
||||||
token.type = Token.UNKNOWN
|
|
||||||
token.target = letter
|
|
||||||
return token
|
|
||||||
} else if (cmp == 0) {
|
|
||||||
token.type = Token.PINYIN
|
|
||||||
offset = UNIHANS.size - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
token.type = Token.PINYIN
|
|
||||||
if (offset < 0) {
|
|
||||||
var begin = 0
|
|
||||||
var end = UNIHANS.size - 1
|
|
||||||
while (begin <= end) {
|
|
||||||
offset = (begin + end) / 2
|
|
||||||
val unihan = UNIHANS[offset].toString()
|
|
||||||
cmp = COLLATOR.compare(letter, unihan)
|
|
||||||
when {
|
|
||||||
cmp == 0 -> break
|
|
||||||
cmp > 0 -> begin = offset + 1
|
|
||||||
else -> end = offset - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cmp < 0) {
|
|
||||||
offset--
|
|
||||||
}
|
|
||||||
|
|
||||||
val pinyin = StringBuilder()
|
|
||||||
for (j in PINYINS[offset].indices) {
|
|
||||||
if (PINYINS[offset][j] == 0.toByte()) break
|
|
||||||
pinyin.append(PINYINS[offset][j].toInt().toChar())
|
|
||||||
}
|
|
||||||
token.target = pinyin.toString()
|
|
||||||
if (TextUtils.isEmpty(token.target)) {
|
|
||||||
token.type = Token.UNKNOWN
|
|
||||||
token.target = token.source
|
|
||||||
}
|
|
||||||
return token
|
|
||||||
}
|
|
||||||
|
|
||||||
fun get(input: String?): ArrayList<Token> {
|
|
||||||
val tokens = ArrayList<Token>()
|
|
||||||
if (!hasChinaCollator || TextUtils.isEmpty(input)) {
|
|
||||||
return tokens
|
|
||||||
}
|
|
||||||
|
|
||||||
val inputLength = input!!.length
|
|
||||||
val sb = StringBuilder()
|
|
||||||
var tokenType = Token.LATIN
|
|
||||||
|
|
||||||
for (i in 0 until inputLength) {
|
|
||||||
val character = input[i]
|
|
||||||
when {
|
|
||||||
character == ' ' -> {
|
|
||||||
if (sb.isNotEmpty()) {
|
|
||||||
addToken(sb, tokens, tokenType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
character < 256.toChar() -> {
|
|
||||||
if (tokenType != Token.LATIN && sb.isNotEmpty()) {
|
|
||||||
addToken(sb, tokens, tokenType)
|
|
||||||
}
|
|
||||||
tokenType = Token.LATIN
|
|
||||||
sb.append(character)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
val t = getToken(character)
|
|
||||||
if (t.type == Token.PINYIN) {
|
|
||||||
if (sb.isNotEmpty()) {
|
|
||||||
addToken(sb, tokens, tokenType)
|
|
||||||
}
|
|
||||||
tokens.add(t)
|
|
||||||
tokenType = Token.PINYIN
|
|
||||||
} else {
|
|
||||||
if (tokenType != t.type && sb.isNotEmpty()) {
|
|
||||||
addToken(sb, tokens, tokenType)
|
|
||||||
}
|
|
||||||
tokenType = t.type
|
|
||||||
sb.append(character)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sb.isNotEmpty()) {
|
|
||||||
addToken(sb, tokens, tokenType)
|
|
||||||
}
|
|
||||||
return tokens
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addToken(sb: StringBuilder, tokens: ArrayList<Token>, tokenType: Int) {
|
|
||||||
val str = sb.toString()
|
|
||||||
tokens.add(Token(tokenType, str, str))
|
|
||||||
sb.setLength(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toPinyinString(string: String?): String? {
|
|
||||||
if (string == null) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
val sb = StringBuilder()
|
|
||||||
val tokens = get(string)
|
|
||||||
for (token in tokens) {
|
|
||||||
sb.append(token.target)
|
|
||||||
}
|
|
||||||
return sb.toString().lowercase()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val TAG = "HanziToPinyin"
|
|
||||||
private const val DEBUG = false
|
|
||||||
|
|
||||||
val UNIHANS = charArrayOf(
|
|
||||||
'阿', '哎', '安', '肮', '凹', '八',
|
|
||||||
'挀', '扳', '邦', '勹', '陂', '奔',
|
|
||||||
'伻', '屄', '边', '灬', '憋', '汃',
|
|
||||||
'冫', '癶', '峬', '嚓', '偲', '参',
|
|
||||||
'仓', '撡', '冊', '嵾', '曽', '曾',
|
|
||||||
'層', '叉', '芆', '辿', '伥', '抄',
|
|
||||||
'车', '抻', '沈', '沉', '阷', '吃',
|
|
||||||
'充', '抽', '出', '欻', '揣', '巛',
|
|
||||||
'刅', '吹', '旾', '逴', '呲', '匆',
|
|
||||||
'凑', '粗', '汆', '崔', '邨', '搓',
|
|
||||||
'咑', '呆', '丹', '当', '刀', '嘚',
|
|
||||||
'扥', '灯', '氐', '嗲', '甸', '刁',
|
|
||||||
'爹', '丁', '丟', '东', '吺', '厾',
|
|
||||||
'耑', '襨', '吨', '多', '妸', '诶',
|
|
||||||
'奀', '鞥', '儿', '发', '帆', '匚',
|
|
||||||
'飞', '分', '丰', '覅', '仏', '紑',
|
|
||||||
'伕', '旮', '侅', '甘', '冈', '皋',
|
|
||||||
'戈', '给', '根', '刯', '工', '勾',
|
|
||||||
'估', '瓜', '乖', '关', '光', '归',
|
|
||||||
'丨', '呙', '哈', '咍', '佄', '夯',
|
|
||||||
'茠', '诃', '黒', '拫', '亨', '噷',
|
|
||||||
'叿', '齁', '乯', '花', '怀', '犿',
|
|
||||||
'巟', '灰', '昏', '吙', '丌', '加',
|
|
||||||
'戋', '江', '艽', '阶', '巾', '坕',
|
|
||||||
'冂', '丩', '凥', '姢', '噘', '军',
|
|
||||||
'咔', '开', '刊', '忼', '尻', '匼',
|
|
||||||
'肎', '劥', '空', '抠', '扝', '夸',
|
|
||||||
'蒯', '宽', '匡', '亏', '坤', '扩',
|
|
||||||
'垃', '来', '兰', '啷', '捞', '肋',
|
|
||||||
'勒', '崚', '刕', '俩', '奁', '良',
|
|
||||||
'撩', '列', '拎', '刢', '溜', '囖',
|
|
||||||
'龙', '瞜', '噜', '娈', '畧', '抡',
|
|
||||||
'罗', '呣', '妈', '埋', '嫚', '牤',
|
|
||||||
'猫', '么', '呅', '门', '甿', '咪',
|
|
||||||
'宀', '喵', '乜', '民', '名', '谬',
|
|
||||||
'摸', '哞', '毪', '嗯', '拏', '腉',
|
|
||||||
'囡', '囔', '孬', '疒', '娞', '恁',
|
|
||||||
'能', '妮', '拈', '嬢', '鸟', '捏',
|
|
||||||
'囜', '宁', '妞', '农', '羺', '奴',
|
|
||||||
'奻', '疟', '黁', '郍', '喔', '讴',
|
|
||||||
'妑', '拍', '眅', '乓', '抛', '呸',
|
|
||||||
'喷', '匉', '丕', '囨', '剽', '氕',
|
|
||||||
'姘', '乒', '钋', '剖', '仆', '七',
|
|
||||||
'掐', '千', '呛', '悄', '癿', '亲',
|
|
||||||
'狅', '芎', '丘', '区', '峑', '缺',
|
|
||||||
'夋', '呥', '穣', '娆', '惹', '人',
|
|
||||||
'扔', '日', '茸', '厹', '邚', '挼',
|
|
||||||
'堧', '婑', '瞤', '捼', '仨', '毢',
|
|
||||||
'三', '桒', '掻', '閪', '森', '僧',
|
|
||||||
'杀', '筛', '山', '伤', '弰', '奢',
|
|
||||||
'申', '莘', '敒', '升', '尸', '収',
|
|
||||||
'书', '刷', '衰', '闩', '双', '谁',
|
|
||||||
'吮', '说', '厶', '忪', '捜', '苏',
|
|
||||||
'狻', '夊', '孙', '唆', '他', '囼',
|
|
||||||
'坍', '汤', '夲', '忑', '熥', '剔',
|
|
||||||
'天', '旫', '帖', '厅', '囲', '偷',
|
|
||||||
'凸', '湍', '推', '吞', '乇', '穵',
|
|
||||||
'歪', '弯', '尣', '危', '昷', '翁',
|
|
||||||
'挝', '乌', '夕', '虲', '仚', '乡',
|
|
||||||
'灱', '些', '心', '星', '凶', '休',
|
|
||||||
'吁', '吅', '削', '坃', '丫', '恹',
|
|
||||||
'央', '幺', '倻', '一', '囙', '应',
|
|
||||||
'哟', '佣', '优', '扜', '囦', '曰',
|
|
||||||
'晕', '筠', '筼', '帀', '災', '兂',
|
|
||||||
'匨', '傮', '则', '贼', '怎', '増',
|
|
||||||
'扎', '捚', '沾', '张', '长', '長',
|
|
||||||
'佋', '蜇', '贞', '争', '之', '峙',
|
|
||||||
'庢', '中', '州', '朱', '抓', '拽',
|
|
||||||
'专', '妆', '隹', '宒', '卓', '乲',
|
|
||||||
'宗', '邹', '租', '钻', '厜', '尊',
|
|
||||||
'昨', '兙', '鿃', '鿄'
|
|
||||||
)
|
|
||||||
|
|
||||||
val PINYINS = arrayOf(
|
|
||||||
byteArrayOf(65, 0, 0, 0, 0, 0), byteArrayOf(65, 73, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(65, 78, 0, 0, 0, 0), byteArrayOf(65, 78, 71, 0, 0, 0),
|
|
||||||
byteArrayOf(65, 79, 0, 0, 0, 0), byteArrayOf(66, 65, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(66, 65, 73, 0, 0, 0), byteArrayOf(66, 65, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(66, 65, 78, 71, 0, 0), byteArrayOf(66, 65, 79, 0, 0, 0),
|
|
||||||
byteArrayOf(66, 69, 73, 0, 0, 0), byteArrayOf(66, 69, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(66, 69, 78, 71, 0, 0), byteArrayOf(66, 73, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(66, 73, 65, 78, 0, 0), byteArrayOf(66, 73, 65, 79, 0, 0),
|
|
||||||
byteArrayOf(66, 73, 69, 0, 0, 0), byteArrayOf(66, 73, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(66, 73, 78, 71, 0, 0), byteArrayOf(66, 79, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(66, 85, 0, 0, 0, 0), byteArrayOf(67, 65, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(67, 65, 73, 0, 0, 0), byteArrayOf(67, 65, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(67, 65, 78, 71, 0, 0), byteArrayOf(67, 65, 79, 0, 0, 0),
|
|
||||||
byteArrayOf(67, 69, 0, 0, 0, 0), byteArrayOf(67, 69, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(67, 69, 78, 71, 0, 0), byteArrayOf(90, 69, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(67, 69, 78, 71, 0, 0), byteArrayOf(67, 72, 65, 0, 0, 0),
|
|
||||||
byteArrayOf(67, 72, 65, 73, 0, 0), byteArrayOf(67, 72, 65, 78, 0, 0),
|
|
||||||
byteArrayOf(67, 72, 65, 78, 71, 0), byteArrayOf(67, 72, 65, 79, 0, 0),
|
|
||||||
byteArrayOf(67, 72, 69, 0, 0, 0), byteArrayOf(67, 72, 69, 78, 0, 0),
|
|
||||||
byteArrayOf(83, 72, 69, 78, 0, 0), byteArrayOf(67, 72, 69, 78, 0, 0),
|
|
||||||
byteArrayOf(67, 72, 69, 78, 71, 0), byteArrayOf(67, 72, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(67, 72, 79, 78, 71, 0), byteArrayOf(67, 72, 79, 85, 0, 0),
|
|
||||||
byteArrayOf(67, 72, 85, 0, 0, 0), byteArrayOf(67, 72, 85, 65, 0, 0),
|
|
||||||
byteArrayOf(67, 72, 85, 65, 73, 0), byteArrayOf(67, 72, 85, 65, 78, 0),
|
|
||||||
byteArrayOf(67, 72, 85, 65, 78, 71), byteArrayOf(67, 72, 85, 73, 0, 0),
|
|
||||||
byteArrayOf(67, 72, 85, 78, 0, 0), byteArrayOf(67, 72, 85, 79, 0, 0),
|
|
||||||
byteArrayOf(67, 73, 0, 0, 0, 0), byteArrayOf(67, 79, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(67, 79, 85, 0, 0, 0), byteArrayOf(67, 85, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(67, 85, 65, 78, 0, 0), byteArrayOf(67, 85, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(67, 85, 78, 0, 0, 0), byteArrayOf(67, 85, 79, 0, 0, 0),
|
|
||||||
byteArrayOf(68, 65, 0, 0, 0, 0), byteArrayOf(68, 65, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(68, 65, 78, 0, 0, 0), byteArrayOf(68, 65, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(68, 65, 79, 0, 0, 0), byteArrayOf(68, 69, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(68, 69, 78, 0, 0, 0), byteArrayOf(68, 69, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(68, 73, 0, 0, 0, 0), byteArrayOf(68, 73, 65, 0, 0, 0),
|
|
||||||
byteArrayOf(68, 73, 65, 78, 0, 0), byteArrayOf(68, 73, 65, 79, 0, 0),
|
|
||||||
byteArrayOf(68, 73, 69, 0, 0, 0), byteArrayOf(68, 73, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(68, 73, 85, 0, 0, 0), byteArrayOf(68, 79, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(68, 79, 85, 0, 0, 0), byteArrayOf(68, 85, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(68, 85, 65, 78, 0, 0), byteArrayOf(68, 85, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(68, 85, 78, 0, 0, 0), byteArrayOf(68, 85, 79, 0, 0, 0),
|
|
||||||
byteArrayOf(69, 0, 0, 0, 0, 0), byteArrayOf(69, 73, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(69, 78, 0, 0, 0, 0), byteArrayOf(69, 78, 71, 0, 0, 0),
|
|
||||||
byteArrayOf(69, 82, 0, 0, 0, 0), byteArrayOf(70, 65, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(70, 65, 78, 0, 0, 0), byteArrayOf(70, 65, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(70, 69, 73, 0, 0, 0), byteArrayOf(70, 69, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(70, 69, 78, 71, 0, 0), byteArrayOf(70, 73, 65, 79, 0, 0),
|
|
||||||
byteArrayOf(70, 79, 0, 0, 0, 0), byteArrayOf(70, 79, 85, 0, 0, 0),
|
|
||||||
byteArrayOf(70, 85, 0, 0, 0, 0), byteArrayOf(71, 65, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(71, 65, 73, 0, 0, 0), byteArrayOf(71, 65, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(71, 65, 78, 71, 0, 0), byteArrayOf(71, 65, 79, 0, 0, 0),
|
|
||||||
byteArrayOf(71, 69, 0, 0, 0, 0), byteArrayOf(71, 69, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(71, 69, 78, 0, 0, 0), byteArrayOf(71, 69, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(71, 79, 78, 71, 0, 0), byteArrayOf(71, 79, 85, 0, 0, 0),
|
|
||||||
byteArrayOf(71, 85, 0, 0, 0, 0), byteArrayOf(71, 85, 65, 0, 0, 0),
|
|
||||||
byteArrayOf(71, 85, 65, 73, 0, 0), byteArrayOf(71, 85, 65, 78, 0, 0),
|
|
||||||
byteArrayOf(71, 85, 65, 78, 71, 0), byteArrayOf(71, 85, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(71, 85, 78, 0, 0, 0), byteArrayOf(71, 85, 79, 0, 0, 0),
|
|
||||||
byteArrayOf(72, 65, 0, 0, 0, 0), byteArrayOf(72, 65, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(72, 65, 78, 0, 0, 0), byteArrayOf(72, 65, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(72, 65, 79, 0, 0, 0), byteArrayOf(72, 69, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(72, 69, 73, 0, 0, 0), byteArrayOf(72, 69, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(72, 69, 78, 71, 0, 0), byteArrayOf(72, 77, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(72, 79, 78, 71, 0, 0), byteArrayOf(72, 79, 85, 0, 0, 0),
|
|
||||||
byteArrayOf(72, 85, 0, 0, 0, 0), byteArrayOf(72, 85, 65, 0, 0, 0),
|
|
||||||
byteArrayOf(72, 85, 65, 73, 0, 0), byteArrayOf(72, 85, 65, 78, 0, 0),
|
|
||||||
byteArrayOf(72, 85, 65, 78, 71, 0), byteArrayOf(72, 85, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(72, 85, 78, 0, 0, 0), byteArrayOf(72, 85, 79, 0, 0, 0),
|
|
||||||
byteArrayOf(74, 73, 0, 0, 0, 0), byteArrayOf(74, 73, 65, 0, 0, 0),
|
|
||||||
byteArrayOf(74, 73, 65, 78, 0, 0), byteArrayOf(74, 73, 65, 78, 71, 0),
|
|
||||||
byteArrayOf(74, 73, 65, 79, 0, 0), byteArrayOf(74, 73, 69, 0, 0, 0),
|
|
||||||
byteArrayOf(74, 73, 78, 0, 0, 0), byteArrayOf(74, 73, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(74, 73, 79, 78, 71, 0), byteArrayOf(74, 73, 85, 0, 0, 0),
|
|
||||||
byteArrayOf(74, 85, 0, 0, 0, 0), byteArrayOf(74, 85, 65, 78, 0, 0),
|
|
||||||
byteArrayOf(74, 85, 69, 0, 0, 0), byteArrayOf(74, 85, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(75, 65, 0, 0, 0, 0), byteArrayOf(75, 65, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(75, 65, 78, 0, 0, 0), byteArrayOf(75, 65, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(75, 65, 79, 0, 0, 0), byteArrayOf(75, 69, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(75, 69, 78, 0, 0, 0), byteArrayOf(75, 69, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(75, 79, 78, 71, 0, 0), byteArrayOf(75, 79, 85, 0, 0, 0),
|
|
||||||
byteArrayOf(75, 85, 0, 0, 0, 0), byteArrayOf(75, 85, 65, 0, 0, 0),
|
|
||||||
byteArrayOf(75, 85, 65, 73, 0, 0), byteArrayOf(75, 85, 65, 78, 0, 0),
|
|
||||||
byteArrayOf(75, 85, 65, 78, 71, 0), byteArrayOf(75, 85, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(75, 85, 78, 0, 0, 0), byteArrayOf(75, 85, 79, 0, 0, 0),
|
|
||||||
byteArrayOf(76, 65, 0, 0, 0, 0), byteArrayOf(76, 65, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(76, 65, 78, 0, 0, 0), byteArrayOf(76, 65, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(76, 65, 79, 0, 0, 0), byteArrayOf(76, 69, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(76, 69, 73, 0, 0, 0), byteArrayOf(76, 69, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(76, 73, 0, 0, 0, 0), byteArrayOf(76, 73, 65, 0, 0, 0),
|
|
||||||
byteArrayOf(76, 73, 65, 78, 0, 0), byteArrayOf(76, 73, 65, 78, 71, 0),
|
|
||||||
byteArrayOf(76, 73, 65, 79, 0, 0), byteArrayOf(76, 73, 69, 0, 0, 0),
|
|
||||||
byteArrayOf(76, 73, 78, 0, 0, 0), byteArrayOf(76, 73, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(76, 73, 85, 0, 0, 0), byteArrayOf(76, 79, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(76, 79, 78, 71, 0, 0), byteArrayOf(76, 79, 85, 0, 0, 0),
|
|
||||||
byteArrayOf(76, 85, 0, 0, 0, 0), byteArrayOf(76, 85, 65, 78, 0, 0),
|
|
||||||
byteArrayOf(76, 85, 69, 0, 0, 0), byteArrayOf(76, 85, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(76, 85, 79, 0, 0, 0), byteArrayOf(77, 0, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(77, 65, 0, 0, 0, 0), byteArrayOf(77, 65, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(77, 65, 78, 0, 0, 0), byteArrayOf(77, 65, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(77, 65, 79, 0, 0, 0), byteArrayOf(77, 69, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(77, 69, 73, 0, 0, 0), byteArrayOf(77, 69, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(77, 69, 78, 71, 0, 0), byteArrayOf(77, 73, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(77, 73, 65, 78, 0, 0), byteArrayOf(77, 73, 65, 79, 0, 0),
|
|
||||||
byteArrayOf(77, 73, 69, 0, 0, 0), byteArrayOf(77, 73, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(77, 73, 78, 71, 0, 0), byteArrayOf(77, 73, 85, 0, 0, 0),
|
|
||||||
byteArrayOf(77, 79, 0, 0, 0, 0), byteArrayOf(77, 79, 85, 0, 0, 0),
|
|
||||||
byteArrayOf(77, 85, 0, 0, 0, 0), byteArrayOf(78, 0, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(78, 65, 0, 0, 0, 0), byteArrayOf(78, 65, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(78, 65, 78, 0, 0, 0), byteArrayOf(78, 65, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(78, 65, 79, 0, 0, 0), byteArrayOf(78, 69, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(78, 69, 73, 0, 0, 0), byteArrayOf(78, 69, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(78, 69, 78, 71, 0, 0), byteArrayOf(78, 73, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(78, 73, 65, 78, 0, 0), byteArrayOf(78, 73, 65, 78, 71, 0),
|
|
||||||
byteArrayOf(78, 73, 65, 79, 0, 0), byteArrayOf(78, 73, 69, 0, 0, 0),
|
|
||||||
byteArrayOf(78, 73, 78, 0, 0, 0), byteArrayOf(78, 73, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(78, 73, 85, 0, 0, 0), byteArrayOf(78, 79, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(78, 79, 85, 0, 0, 0), byteArrayOf(78, 85, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(78, 85, 65, 78, 0, 0), byteArrayOf(78, 85, 69, 0, 0, 0),
|
|
||||||
byteArrayOf(78, 85, 78, 0, 0, 0), byteArrayOf(78, 85, 79, 0, 0, 0),
|
|
||||||
byteArrayOf(79, 0, 0, 0, 0, 0), byteArrayOf(79, 85, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(80, 65, 0, 0, 0, 0), byteArrayOf(80, 65, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(80, 65, 78, 0, 0, 0), byteArrayOf(80, 65, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(80, 65, 79, 0, 0, 0), byteArrayOf(80, 69, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(80, 69, 78, 0, 0, 0), byteArrayOf(80, 69, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(80, 73, 0, 0, 0, 0), byteArrayOf(80, 73, 65, 78, 0, 0),
|
|
||||||
byteArrayOf(80, 73, 65, 79, 0, 0), byteArrayOf(80, 73, 69, 0, 0, 0),
|
|
||||||
byteArrayOf(80, 73, 78, 0, 0, 0), byteArrayOf(80, 73, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(80, 79, 0, 0, 0, 0), byteArrayOf(80, 79, 85, 0, 0, 0),
|
|
||||||
byteArrayOf(80, 85, 0, 0, 0, 0), byteArrayOf(81, 73, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(81, 73, 65, 0, 0, 0), byteArrayOf(81, 73, 65, 78, 0, 0),
|
|
||||||
byteArrayOf(81, 73, 65, 78, 71, 0), byteArrayOf(81, 73, 65, 79, 0, 0),
|
|
||||||
byteArrayOf(81, 73, 69, 0, 0, 0), byteArrayOf(81, 73, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(81, 73, 78, 71, 0, 0), byteArrayOf(81, 73, 79, 78, 71, 0),
|
|
||||||
byteArrayOf(81, 73, 85, 0, 0, 0), byteArrayOf(81, 85, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(81, 85, 65, 78, 0, 0), byteArrayOf(81, 85, 69, 0, 0, 0),
|
|
||||||
byteArrayOf(81, 85, 78, 0, 0, 0), byteArrayOf(82, 65, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(82, 65, 78, 71, 0, 0), byteArrayOf(82, 65, 79, 0, 0, 0),
|
|
||||||
byteArrayOf(82, 69, 0, 0, 0, 0), byteArrayOf(82, 69, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(82, 69, 78, 71, 0, 0), byteArrayOf(82, 73, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(82, 79, 78, 71, 0, 0), byteArrayOf(82, 79, 85, 0, 0, 0),
|
|
||||||
byteArrayOf(82, 85, 0, 0, 0, 0), byteArrayOf(82, 85, 65, 0, 0, 0),
|
|
||||||
byteArrayOf(82, 85, 65, 78, 0, 0), byteArrayOf(82, 85, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(82, 85, 78, 0, 0, 0), byteArrayOf(82, 85, 79, 0, 0, 0),
|
|
||||||
byteArrayOf(83, 65, 0, 0, 0, 0), byteArrayOf(83, 65, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(83, 65, 78, 0, 0, 0), byteArrayOf(83, 65, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(83, 65, 79, 0, 0, 0), byteArrayOf(83, 69, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(83, 69, 78, 0, 0, 0), byteArrayOf(83, 69, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(83, 72, 65, 0, 0, 0), byteArrayOf(83, 72, 65, 73, 0, 0),
|
|
||||||
byteArrayOf(83, 72, 65, 78, 0, 0), byteArrayOf(83, 72, 65, 78, 71, 0),
|
|
||||||
byteArrayOf(83, 72, 65, 79, 0, 0), byteArrayOf(83, 72, 69, 0, 0, 0),
|
|
||||||
byteArrayOf(83, 72, 69, 78, 0, 0), byteArrayOf(88, 73, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(83, 72, 69, 78, 0, 0), byteArrayOf(83, 72, 69, 78, 71, 0),
|
|
||||||
byteArrayOf(83, 72, 73, 0, 0, 0), byteArrayOf(83, 72, 79, 85, 0, 0),
|
|
||||||
byteArrayOf(83, 72, 85, 0, 0, 0), byteArrayOf(83, 72, 85, 65, 0, 0),
|
|
||||||
byteArrayOf(83, 72, 85, 65, 73, 0), byteArrayOf(83, 72, 85, 65, 78, 0),
|
|
||||||
byteArrayOf(83, 72, 85, 65, 78, 71), byteArrayOf(83, 72, 85, 73, 0, 0),
|
|
||||||
byteArrayOf(83, 72, 85, 78, 0, 0), byteArrayOf(83, 72, 85, 79, 0, 0),
|
|
||||||
byteArrayOf(83, 73, 0, 0, 0, 0), byteArrayOf(83, 79, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(83, 79, 85, 0, 0, 0), byteArrayOf(83, 85, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(83, 85, 65, 78, 0, 0), byteArrayOf(83, 85, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(83, 85, 78, 0, 0, 0), byteArrayOf(83, 85, 79, 0, 0, 0),
|
|
||||||
byteArrayOf(84, 65, 0, 0, 0, 0), byteArrayOf(84, 65, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(84, 65, 78, 0, 0, 0), byteArrayOf(84, 65, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(84, 65, 79, 0, 0, 0), byteArrayOf(84, 69, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(84, 69, 78, 71, 0, 0), byteArrayOf(84, 73, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(84, 73, 65, 78, 0, 0), byteArrayOf(84, 73, 65, 79, 0, 0),
|
|
||||||
byteArrayOf(84, 73, 69, 0, 0, 0), byteArrayOf(84, 73, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(84, 79, 78, 71, 0, 0), byteArrayOf(84, 79, 85, 0, 0, 0),
|
|
||||||
byteArrayOf(84, 85, 0, 0, 0, 0), byteArrayOf(84, 85, 65, 78, 0, 0),
|
|
||||||
byteArrayOf(84, 85, 73, 0, 0, 0), byteArrayOf(84, 85, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(84, 85, 79, 0, 0, 0), byteArrayOf(87, 65, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(87, 65, 73, 0, 0, 0), byteArrayOf(87, 65, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(87, 65, 78, 71, 0, 0), byteArrayOf(87, 69, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(87, 69, 78, 0, 0, 0), byteArrayOf(87, 69, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(87, 79, 0, 0, 0, 0), byteArrayOf(87, 85, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(88, 73, 0, 0, 0, 0), byteArrayOf(88, 73, 65, 0, 0, 0),
|
|
||||||
byteArrayOf(88, 73, 65, 78, 0, 0), byteArrayOf(88, 73, 65, 78, 71, 0),
|
|
||||||
byteArrayOf(88, 73, 65, 79, 0, 0), byteArrayOf(88, 73, 69, 0, 0, 0),
|
|
||||||
byteArrayOf(88, 73, 78, 0, 0, 0), byteArrayOf(88, 73, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(88, 73, 79, 78, 71, 0), byteArrayOf(88, 73, 85, 0, 0, 0),
|
|
||||||
byteArrayOf(88, 85, 0, 0, 0, 0), byteArrayOf(88, 85, 65, 78, 0, 0),
|
|
||||||
byteArrayOf(88, 85, 69, 0, 0, 0), byteArrayOf(88, 85, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(89, 65, 0, 0, 0, 0), byteArrayOf(89, 65, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(89, 65, 78, 71, 0, 0), byteArrayOf(89, 65, 79, 0, 0, 0),
|
|
||||||
byteArrayOf(89, 69, 0, 0, 0, 0), byteArrayOf(89, 73, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(89, 73, 78, 0, 0, 0), byteArrayOf(89, 73, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(89, 79, 0, 0, 0, 0), byteArrayOf(89, 79, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(89, 79, 85, 0, 0, 0), byteArrayOf(89, 85, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(89, 85, 65, 78, 0, 0), byteArrayOf(89, 85, 69, 0, 0, 0),
|
|
||||||
byteArrayOf(89, 85, 78, 0, 0, 0), byteArrayOf(74, 85, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(89, 85, 78, 0, 0, 0), byteArrayOf(90, 65, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(90, 65, 73, 0, 0, 0), byteArrayOf(90, 65, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(90, 65, 78, 71, 0, 0), byteArrayOf(90, 65, 79, 0, 0, 0),
|
|
||||||
byteArrayOf(90, 69, 0, 0, 0, 0), byteArrayOf(90, 69, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(90, 69, 78, 0, 0, 0), byteArrayOf(90, 69, 78, 71, 0, 0),
|
|
||||||
byteArrayOf(90, 72, 65, 0, 0, 0), byteArrayOf(90, 72, 65, 73, 0, 0),
|
|
||||||
byteArrayOf(90, 72, 65, 78, 0, 0), byteArrayOf(90, 72, 65, 78, 71, 0),
|
|
||||||
byteArrayOf(67, 72, 65, 78, 71, 0), byteArrayOf(90, 72, 65, 78, 71, 0),
|
|
||||||
byteArrayOf(90, 72, 65, 79, 0, 0), byteArrayOf(90, 72, 69, 0, 0, 0),
|
|
||||||
byteArrayOf(90, 72, 69, 78, 0, 0), byteArrayOf(90, 72, 69, 78, 71, 0),
|
|
||||||
byteArrayOf(90, 72, 73, 0, 0, 0), byteArrayOf(83, 72, 73, 0, 0, 0),
|
|
||||||
byteArrayOf(90, 72, 73, 0, 0, 0), byteArrayOf(90, 72, 79, 78, 71, 0),
|
|
||||||
byteArrayOf(90, 72, 79, 85, 0, 0), byteArrayOf(90, 72, 85, 0, 0, 0),
|
|
||||||
byteArrayOf(90, 72, 85, 65, 0, 0), byteArrayOf(90, 72, 85, 65, 73, 0),
|
|
||||||
byteArrayOf(90, 72, 85, 65, 78, 0), byteArrayOf(90, 72, 85, 65, 78, 71),
|
|
||||||
byteArrayOf(90, 72, 85, 73, 0, 0), byteArrayOf(90, 72, 85, 78, 0, 0),
|
|
||||||
byteArrayOf(90, 72, 85, 79, 0, 0), byteArrayOf(90, 73, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(90, 79, 78, 71, 0, 0), byteArrayOf(90, 79, 85, 0, 0, 0),
|
|
||||||
byteArrayOf(90, 85, 0, 0, 0, 0), byteArrayOf(90, 85, 65, 78, 0, 0),
|
|
||||||
byteArrayOf(90, 85, 73, 0, 0, 0), byteArrayOf(90, 85, 78, 0, 0, 0),
|
|
||||||
byteArrayOf(90, 85, 79, 0, 0, 0), byteArrayOf(0, 0, 0, 0, 0, 0),
|
|
||||||
byteArrayOf(83, 72, 65, 78, 0, 0), byteArrayOf(0, 0, 0, 0, 0, 0)
|
|
||||||
)
|
|
||||||
|
|
||||||
private const val FIRST_PINYIN_UNIHAN = "阿"
|
|
||||||
private const val LAST_PINYIN_UNIHAN = "鿿"
|
|
||||||
|
|
||||||
private val COLLATOR: Collator = Collator.getInstance(Locale.CHINA)
|
|
||||||
|
|
||||||
private var sInstance: HanziToPinyin? = null
|
|
||||||
|
|
||||||
fun getInstance(): HanziToPinyin {
|
|
||||||
synchronized(HanziToPinyin::class.java) {
|
|
||||||
if (sInstance != null) {
|
|
||||||
return sInstance!!
|
|
||||||
}
|
|
||||||
|
|
||||||
val locale = Collator.getAvailableLocales()
|
|
||||||
for (value in locale) {
|
|
||||||
if (value == Locale.CHINA || value.language.contains("zh")) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(TAG, "Self validation. Result: ${doSelfValidation()}")
|
|
||||||
}
|
|
||||||
sInstance = HanziToPinyin(true)
|
|
||||||
return sInstance!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sInstance == null) {
|
|
||||||
if (Locale.CHINA == Locale.getDefault()) {
|
|
||||||
sInstance = HanziToPinyin(true)
|
|
||||||
return sInstance!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.w(TAG, "There is no Chinese collator, HanziToPinyin is disabled")
|
|
||||||
sInstance = HanziToPinyin(false)
|
|
||||||
return sInstance!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun doSelfValidation(): Boolean {
|
|
||||||
val lastChar = UNIHANS[0]
|
|
||||||
var lastString = lastChar.toString()
|
|
||||||
for (c in UNIHANS) {
|
|
||||||
if (lastChar == c) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val curString = c.toString()
|
|
||||||
val cmp = COLLATOR.compare(lastString, curString)
|
|
||||||
if (cmp >= 0) {
|
|
||||||
Log.e(
|
|
||||||
TAG,
|
|
||||||
"Internal error in Unihan table. The last string \"$lastString\" " +
|
|
||||||
"is greater than current string \"$curString\"."
|
|
||||||
)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
lastString = curString
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -13,6 +13,7 @@ import android.util.Log
|
||||||
import com.topjohnwu.superuser.CallbackList
|
import com.topjohnwu.superuser.CallbackList
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import com.topjohnwu.superuser.ShellUtils
|
import com.topjohnwu.superuser.ShellUtils
|
||||||
|
import com.topjohnwu.superuser.io.SuFile
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
@ -21,6 +22,7 @@ import com.sukisu.ultra.Natives
|
||||||
import com.sukisu.ultra.ksuApp
|
import com.sukisu.ultra.ksuApp
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -97,13 +99,6 @@ fun execKsud(args: String, newShell: Boolean = false): Boolean {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getFeatureStatus(feature: String): String = withContext(Dispatchers.IO) {
|
|
||||||
val shell = getRootShell()
|
|
||||||
val out = shell.newJob()
|
|
||||||
.add("${getKsuDaemonPath()} feature check $feature").to(ArrayList<String>(), null).exec().out
|
|
||||||
out.firstOrNull()?.trim().orEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun install() {
|
fun install() {
|
||||||
val start = SystemClock.elapsedRealtime()
|
val start = SystemClock.elapsedRealtime()
|
||||||
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so").absolutePath
|
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so").absolutePath
|
||||||
|
|
@ -114,8 +109,8 @@ fun install() {
|
||||||
fun listModules(): String {
|
fun listModules(): String {
|
||||||
val shell = getRootShell()
|
val shell = getRootShell()
|
||||||
|
|
||||||
val out = shell.newJob()
|
val out =
|
||||||
.add("${getKsuDaemonPath()} module list").to(ArrayList(), null).exec().out
|
shell.newJob().add("${getKsuDaemonPath()} module list").to(ArrayList(), null).exec().out
|
||||||
return out.joinToString("\n").ifBlank { "[]" }
|
return out.joinToString("\n").ifBlank { "[]" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,13 +151,6 @@ fun restoreModule(id: String): Boolean {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun undoUninstallModule(id: String): Boolean {
|
|
||||||
val cmd = "module undo-uninstall $id"
|
|
||||||
val result = execKsud(cmd, true)
|
|
||||||
Log.i(TAG, "undo uninstall module $id result: $result")
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun flashWithIO(
|
private fun flashWithIO(
|
||||||
cmd: String,
|
cmd: String,
|
||||||
onStdout: (String) -> Unit,
|
onStdout: (String) -> Unit,
|
||||||
|
|
@ -681,14 +669,15 @@ fun readUidScannerFile(): Boolean {
|
||||||
return try {
|
return try {
|
||||||
ShellUtils.fastCmd(shell, "cat /data/adb/ksu/.uid_scanner").trim() == "1"
|
ShellUtils.fastCmd(shell, "cat /data/adb/ksu/.uid_scanner").trim() == "1"
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addUmountPath(path: String, flags: Int): Boolean {
|
fun addUmountPath(path: String, checkMnt: Boolean, flags: Int): Boolean {
|
||||||
val shell = getRootShell()
|
val shell = getRootShell()
|
||||||
|
val checkMntFlag = if (checkMnt) "--check-mnt" else ""
|
||||||
val flagsArg = if (flags >= 0) "--flags $flags" else ""
|
val flagsArg = if (flags >= 0) "--flags $flags" else ""
|
||||||
val cmd = "${getKsuDaemonPath()} umount add $path $flagsArg"
|
val cmd = "${getKsuDaemonPath()} umount add $path $checkMntFlag $flagsArg"
|
||||||
val result = ShellUtils.fastCmdResult(shell, cmd)
|
val result = ShellUtils.fastCmdResult(shell, cmd)
|
||||||
Log.i(TAG, "add umount path $path result: $result")
|
Log.i(TAG, "add umount path $path result: $result")
|
||||||
return result
|
return result
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ class ModuleViewModel : ViewModel() {
|
||||||
).thenBy(Collator.getInstance(Locale.getDefault()), ModuleInfo::id)
|
).thenBy(Collator.getInstance(Locale.getDefault()), ModuleInfo::id)
|
||||||
modules.filter {
|
modules.filter {
|
||||||
it.id.contains(search, true) || it.name.contains(search, true) || HanziToPinyin.getInstance()
|
it.id.contains(search, true) || it.name.contains(search, true) || HanziToPinyin.getInstance()
|
||||||
.toPinyinString(it.name)?.contains(search, true) == true
|
.toPinyinString(it.name).contains(search, true)
|
||||||
}.sortedWith(comparator).also {
|
}.sortedWith(comparator).also {
|
||||||
isRefreshing = false
|
isRefreshing = false
|
||||||
}
|
}
|
||||||
|
|
@ -143,15 +143,15 @@ class ModuleViewModel : ViewModel() {
|
||||||
obj.optString("name"),
|
obj.optString("name"),
|
||||||
obj.optString("author", "Unknown"),
|
obj.optString("author", "Unknown"),
|
||||||
obj.optString("version", "Unknown"),
|
obj.optString("version", "Unknown"),
|
||||||
obj.getIntCompat("versionCode", 0),
|
obj.optInt("versionCode", 0),
|
||||||
obj.optString("description"),
|
obj.optString("description"),
|
||||||
obj.getBooleanCompat("enabled"),
|
obj.getBoolean("enabled"),
|
||||||
obj.getBooleanCompat("update"),
|
obj.getBoolean("update"),
|
||||||
obj.getBooleanCompat("remove"),
|
obj.getBoolean("remove"),
|
||||||
obj.optString("updateJson"),
|
obj.optString("updateJson"),
|
||||||
obj.getBooleanCompat("web"),
|
obj.optBoolean("web"),
|
||||||
obj.getBooleanCompat("action"),
|
obj.optBoolean("action"),
|
||||||
obj.optString("dir_id", obj.getString("id"))
|
obj.getString("dir_id")
|
||||||
)
|
)
|
||||||
}.toList()
|
}.toList()
|
||||||
|
|
||||||
|
|
@ -469,26 +469,6 @@ class ModuleSizeCache(context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun JSONObject.getBooleanCompat(key: String, default: Boolean = false): Boolean {
|
|
||||||
if (!has(key)) return default
|
|
||||||
return when (val value = opt(key)) {
|
|
||||||
is Boolean -> value
|
|
||||||
is String -> value.equals("true", ignoreCase = true) || value == "1"
|
|
||||||
is Number -> value.toInt() != 0
|
|
||||||
else -> default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun JSONObject.getIntCompat(key: String, default: Int = 0): Int {
|
|
||||||
if (!has(key)) return default
|
|
||||||
return when (val value = opt(key)) {
|
|
||||||
is Int -> value
|
|
||||||
is Number -> value.toInt()
|
|
||||||
is String -> value.toIntOrNull() ?: default
|
|
||||||
else -> default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式化文件大小的工具函数
|
* 格式化文件大小的工具函数
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import com.topjohnwu.superuser.Shell
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
import java.text.Collator
|
import java.text.Collator
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.LinkedBlockingQueue
|
import java.util.concurrent.LinkedBlockingQueue
|
||||||
|
|
@ -26,10 +27,6 @@ import java.util.concurrent.TimeUnit
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
import com.sukisu.zako.IKsuInterface
|
import com.sukisu.zako.IKsuInterface
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
|
||||||
import kotlinx.parcelize.IgnoredOnParcel
|
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
|
|
||||||
enum class AppCategory(val displayNameRes: Int, val persistKey: String) {
|
enum class AppCategory(val displayNameRes: Int, val persistKey: String) {
|
||||||
ALL(com.sukisu.ultra.R.string.category_all_apps, "ALL"),
|
ALL(com.sukisu.ultra.R.string.category_all_apps, "ALL"),
|
||||||
|
|
@ -61,8 +58,7 @@ class SuperUserViewModel : ViewModel() {
|
||||||
private const val TAG = "SuperUserViewModel"
|
private const val TAG = "SuperUserViewModel"
|
||||||
private val appsLock = Any()
|
private val appsLock = Any()
|
||||||
var apps by mutableStateOf<List<AppInfo>>(emptyList())
|
var apps by mutableStateOf<List<AppInfo>>(emptyList())
|
||||||
private val _isAppListLoaded = MutableStateFlow(false)
|
var appGroups by mutableStateOf<List<AppGroup>>(emptyList())
|
||||||
val isAppListLoaded = _isAppListLoaded.asStateFlow()
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getAppIconDrawable(context: Context, packageName: String): Drawable? {
|
fun getAppIconDrawable(context: Context, packageName: String): Drawable? {
|
||||||
|
|
@ -71,8 +67,6 @@ class SuperUserViewModel : ViewModel() {
|
||||||
?.packageInfo?.applicationInfo?.loadIcon(context.packageManager)
|
?.packageInfo?.applicationInfo?.loadIcon(context.packageManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
var appGroups by mutableStateOf<List<AppGroup>>(emptyList())
|
|
||||||
|
|
||||||
private const val PREFS_NAME = "settings"
|
private const val PREFS_NAME = "settings"
|
||||||
private const val KEY_SHOW_SYSTEM_APPS = "show_system_apps"
|
private const val KEY_SHOW_SYSTEM_APPS = "show_system_apps"
|
||||||
private const val KEY_SELECTED_CATEGORY = "selected_category"
|
private const val KEY_SELECTED_CATEGORY = "selected_category"
|
||||||
|
|
@ -83,36 +77,31 @@ class SuperUserViewModel : ViewModel() {
|
||||||
private const val BATCH_SIZE = 20
|
private const val BATCH_SIZE = 20
|
||||||
}
|
}
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class AppInfo(
|
data class AppInfo(
|
||||||
val label: String,
|
val label: String,
|
||||||
val packageInfo: PackageInfo,
|
val packageInfo: PackageInfo,
|
||||||
val profile: Natives.Profile?,
|
val profile: Natives.Profile?,
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
@IgnoredOnParcel
|
val packageName: String get() = packageInfo.packageName
|
||||||
val packageName: String = packageInfo.packageName
|
val uid: Int get() = packageInfo.applicationInfo!!.uid
|
||||||
@IgnoredOnParcel
|
|
||||||
val uid: Int = packageInfo.applicationInfo!!.uid
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class AppGroup(
|
data class AppGroup(
|
||||||
val uid: Int,
|
val uid: Int,
|
||||||
val apps: List<AppInfo>,
|
val apps: List<AppInfo>,
|
||||||
val profile: Natives.Profile?
|
val profile: Natives.Profile?
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
@IgnoredOnParcel
|
val mainApp: AppInfo get() = apps.first()
|
||||||
val mainApp: AppInfo = apps.first()
|
val packageNames: List<String> get() = apps.map { it.packageName }
|
||||||
@IgnoredOnParcel
|
val allowSu: Boolean get() = profile?.allowSu == true
|
||||||
val packageNames: List<String> = apps.map { it.packageName }
|
|
||||||
@IgnoredOnParcel
|
val userName: String? get() = Natives.getUserName(uid)
|
||||||
val allowSu: Boolean = profile?.allowSu == true
|
val hasCustomProfile: Boolean
|
||||||
@IgnoredOnParcel
|
get() = profile?.let {
|
||||||
val userName: String? = Natives.getUserName(uid)
|
if (it.allowSu) !it.rootUseDefault else !it.nonRootUseDefault
|
||||||
@IgnoredOnParcel
|
} ?: false
|
||||||
val hasCustomProfile : Boolean = profile?.let { if (it.allowSu) !it.rootUseDefault else !it.nonRootUseDefault } ?: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val appProcessingThreadPool = ThreadPoolExecutor(
|
private val appProcessingThreadPool = ThreadPoolExecutor(
|
||||||
|
|
@ -342,10 +331,6 @@ class SuperUserViewModel : ViewModel() {
|
||||||
|
|
||||||
stopKsuService()
|
stopKsuService()
|
||||||
|
|
||||||
synchronized(appsLock) {
|
|
||||||
_isAppListLoaded.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
appListMutex.withLock {
|
appListMutex.withLock {
|
||||||
val filteredApps = result.filter { it.packageName != ksuApp.packageName }
|
val filteredApps = result.filter { it.packageName != ksuApp.packageName }
|
||||||
apps = filteredApps
|
apps = filteredApps
|
||||||
|
|
@ -361,7 +346,7 @@ class SuperUserViewModel : ViewModel() {
|
||||||
group.apps.any { app ->
|
group.apps.any { app ->
|
||||||
app.label.contains(search, true) ||
|
app.label.contains(search, true) ||
|
||||||
app.packageName.contains(search, true) ||
|
app.packageName.contains(search, true) ||
|
||||||
HanziToPinyin.getInstance().toPinyinString(app.label)?.contains(search, true) == true
|
HanziToPinyin.getInstance().toPinyinString(app.label).contains(search, true)
|
||||||
}
|
}
|
||||||
}.filter { group ->
|
}.filter { group ->
|
||||||
group.uid == 2000 || showSystemApps ||
|
group.uid == 2000 || showSystemApps ||
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.sukisu.ultra.ui.webui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.util.LruCache;
|
||||||
|
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel;
|
||||||
|
|
||||||
|
public class AppIconUtil {
|
||||||
|
// Limit cache size to 200 icons
|
||||||
|
private static final int CACHE_SIZE = 200;
|
||||||
|
private static final LruCache<String, Bitmap> iconCache = new LruCache<>(CACHE_SIZE);
|
||||||
|
|
||||||
|
public static synchronized Bitmap loadAppIconSync(Context context, String packageName, int sizePx) {
|
||||||
|
Bitmap cached = iconCache.get(packageName);
|
||||||
|
if (cached != null) return cached;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Drawable drawable = SuperUserViewModel.getAppIconDrawable(context, packageName);
|
||||||
|
if (drawable == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Bitmap raw = drawableToBitmap(drawable, sizePx);
|
||||||
|
Bitmap icon = Bitmap.createScaledBitmap(raw, sizePx, sizePx, true);
|
||||||
|
if (raw != icon) raw.recycle();
|
||||||
|
iconCache.put(packageName, icon);
|
||||||
|
return icon;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Bitmap drawableToBitmap(Drawable drawable, int size) {
|
||||||
|
if (drawable instanceof BitmapDrawable) return ((BitmapDrawable) drawable).getBitmap();
|
||||||
|
|
||||||
|
int width = drawable.getIntrinsicWidth() > 0 ? drawable.getIntrinsicWidth() : size;
|
||||||
|
int height = drawable.getIntrinsicHeight() > 0 ? drawable.getIntrinsicHeight() : size;
|
||||||
|
|
||||||
|
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(bmp);
|
||||||
|
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||||
|
drawable.draw(canvas);
|
||||||
|
return bmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
package com.sukisu.ultra.ui.webui
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.Canvas
|
|
||||||
import android.graphics.drawable.BitmapDrawable
|
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.util.LruCache
|
|
||||||
import androidx.core.graphics.createBitmap
|
|
||||||
import androidx.core.graphics.scale
|
|
||||||
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel.Companion.getAppIconDrawable
|
|
||||||
|
|
||||||
object AppIconUtil {
|
|
||||||
// Limit cache size to 200 icons
|
|
||||||
private const val CACHE_SIZE = 200
|
|
||||||
private val iconCache = LruCache<String?, Bitmap?>(CACHE_SIZE)
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
fun loadAppIconSync(context: Context, packageName: String, sizePx: Int): Bitmap? {
|
|
||||||
val cached = iconCache.get(packageName)
|
|
||||||
if (cached != null) return cached
|
|
||||||
|
|
||||||
try {
|
|
||||||
val drawable = getAppIconDrawable(context, packageName) ?: return null
|
|
||||||
val raw = drawableToBitmap(drawable, sizePx)
|
|
||||||
val icon = raw.scale(sizePx, sizePx)
|
|
||||||
iconCache.put(packageName, icon)
|
|
||||||
return icon
|
|
||||||
} catch (_: Exception) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun drawableToBitmap(drawable: Drawable, size: Int): Bitmap {
|
|
||||||
if (drawable is BitmapDrawable) return drawable.bitmap
|
|
||||||
|
|
||||||
val width = if (drawable.intrinsicWidth > 0) drawable.intrinsicWidth else size
|
|
||||||
val height = if (drawable.intrinsicHeight > 0) drawable.intrinsicHeight else size
|
|
||||||
|
|
||||||
val bmp = createBitmap(width, height)
|
|
||||||
val canvas = Canvas(bmp)
|
|
||||||
drawable.setBounds(0, 0, canvas.width, canvas.height)
|
|
||||||
drawable.draw(canvas)
|
|
||||||
return bmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
package com.sukisu.ultra.ui.webui
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insets data class from GitHub@MMRLApp/WebUI-X-Portable
|
|
||||||
*
|
|
||||||
* Data class representing insets (top, bottom, left, right) for a view.
|
|
||||||
*
|
|
||||||
* This class provides methods to generate CSS code that can be injected into a WebView
|
|
||||||
* to apply these insets as CSS variables. This is useful for adapting web content
|
|
||||||
* to the safe areas of a device screen, considering notches, status bars, and navigation bars.
|
|
||||||
*
|
|
||||||
* @property top The top inset value in pixels.
|
|
||||||
* @property bottom The bottom inset value in pixels.
|
|
||||||
* @property left The left inset value in pixels.
|
|
||||||
* @property right The right inset value in pixels.
|
|
||||||
*/
|
|
||||||
data class Insets(
|
|
||||||
val top: Int,
|
|
||||||
val bottom: Int,
|
|
||||||
val left: Int,
|
|
||||||
val right: Int,
|
|
||||||
) {
|
|
||||||
val css
|
|
||||||
get() = buildString {
|
|
||||||
appendLine(":root {")
|
|
||||||
appendLine("\t--safe-area-inset-top: ${top}px;")
|
|
||||||
appendLine("\t--safe-area-inset-right: ${right}px;")
|
|
||||||
appendLine("\t--safe-area-inset-bottom: ${bottom}px;")
|
|
||||||
appendLine("\t--safe-area-inset-left: ${left}px;")
|
|
||||||
appendLine("\t--window-inset-top: var(--safe-area-inset-top, 0px);")
|
|
||||||
appendLine("\t--window-inset-bottom: var(--safe-area-inset-bottom, 0px);")
|
|
||||||
appendLine("\t--window-inset-left: var(--safe-area-inset-left, 0px);")
|
|
||||||
appendLine("\t--window-inset-right: var(--safe-area-inset-right, 0px);")
|
|
||||||
appendLine("\t--f7-safe-area-top: var(--window-inset-top, 0px) !important;")
|
|
||||||
appendLine("\t--f7-safe-area-bottom: var(--window-inset-bottom, 0px) !important;")
|
|
||||||
appendLine("\t--f7-safe-area-left: var(--window-inset-left, 0px) !important;")
|
|
||||||
appendLine("\t--f7-safe-area-right: var(--window-inset-right, 0px) !important;")
|
|
||||||
append("}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sukisu.ultra.ui.webui;
|
||||||
|
|
||||||
|
import java.net.URLConnection;
|
||||||
|
|
||||||
|
class MimeUtil {
|
||||||
|
|
||||||
|
public static String getMimeFromFileName(String fileName) {
|
||||||
|
if (fileName == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copying the logic and mapping that Chromium follows.
|
||||||
|
// First we check against the OS (this is a limited list by default)
|
||||||
|
// but app developers can extend this.
|
||||||
|
// We then check against a list of hardcoded mime types above if the
|
||||||
|
// OS didn't provide a result.
|
||||||
|
String mimeType = URLConnection.guessContentTypeFromName(fileName);
|
||||||
|
|
||||||
|
if (mimeType != null) {
|
||||||
|
return mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return guessHardcodedMime(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should keep this map in sync with the lists under
|
||||||
|
// //net/base/mime_util.cc in Chromium.
|
||||||
|
// A bunch of the mime types don't really apply to Android land
|
||||||
|
// like word docs so feel free to filter out where necessary.
|
||||||
|
private static String guessHardcodedMime(String fileName) {
|
||||||
|
int finalFullStop = fileName.lastIndexOf('.');
|
||||||
|
if (finalFullStop == -1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String extension = fileName.substring(finalFullStop + 1).toLowerCase();
|
||||||
|
|
||||||
|
return switch (extension) {
|
||||||
|
case "webm" -> "video/webm";
|
||||||
|
case "mpeg", "mpg" -> "video/mpeg";
|
||||||
|
case "mp3" -> "audio/mpeg";
|
||||||
|
case "wasm" -> "application/wasm";
|
||||||
|
case "xhtml", "xht", "xhtm" -> "application/xhtml+xml";
|
||||||
|
case "flac" -> "audio/flac";
|
||||||
|
case "ogg", "oga", "opus" -> "audio/ogg";
|
||||||
|
case "wav" -> "audio/wav";
|
||||||
|
case "m4a" -> "audio/x-m4a";
|
||||||
|
case "gif" -> "image/gif";
|
||||||
|
case "jpeg", "jpg", "jfif", "pjpeg", "pjp" -> "image/jpeg";
|
||||||
|
case "png" -> "image/png";
|
||||||
|
case "apng" -> "image/apng";
|
||||||
|
case "svg", "svgz" -> "image/svg+xml";
|
||||||
|
case "webp" -> "image/webp";
|
||||||
|
case "mht", "mhtml" -> "multipart/related";
|
||||||
|
case "css" -> "text/css";
|
||||||
|
case "html", "htm", "shtml", "shtm", "ehtml" -> "text/html";
|
||||||
|
case "js", "mjs" -> "application/javascript";
|
||||||
|
case "xml" -> "text/xml";
|
||||||
|
case "mp4", "m4v" -> "video/mp4";
|
||||||
|
case "ogv", "ogm" -> "video/ogg";
|
||||||
|
case "ico" -> "image/x-icon";
|
||||||
|
case "woff" -> "application/font-woff";
|
||||||
|
case "gz", "tgz" -> "application/gzip";
|
||||||
|
case "json" -> "application/json";
|
||||||
|
case "pdf" -> "application/pdf";
|
||||||
|
case "zip" -> "application/zip";
|
||||||
|
case "bmp" -> "image/bmp";
|
||||||
|
case "tiff", "tif" -> "image/tiff";
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.sukisu.ultra.ui.webui
|
|
||||||
|
|
||||||
import java.net.URLConnection
|
|
||||||
|
|
||||||
internal object MimeUtil {
|
|
||||||
fun getMimeFromFileName(fileName: String?): String? {
|
|
||||||
if (fileName == null) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val mimeType = URLConnection.guessContentTypeFromName(fileName)
|
|
||||||
if (mimeType != null) {
|
|
||||||
return mimeType
|
|
||||||
}
|
|
||||||
|
|
||||||
return guessHardcodedMime(fileName)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun guessHardcodedMime(fileName: String): String? {
|
|
||||||
val finalFullStop = fileName.lastIndexOf('.')
|
|
||||||
if (finalFullStop == -1) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val extension = fileName.substring(finalFullStop + 1).lowercase()
|
|
||||||
|
|
||||||
return when (extension) {
|
|
||||||
"webm" -> "video/webm"
|
|
||||||
"mpeg", "mpg" -> "video/mpeg"
|
|
||||||
"mp3" -> "audio/mpeg"
|
|
||||||
"wasm" -> "application/wasm"
|
|
||||||
"xhtml", "xht", "xhtm" -> "application/xhtml+xml"
|
|
||||||
"flac" -> "audio/flac"
|
|
||||||
"ogg", "oga", "opus" -> "audio/ogg"
|
|
||||||
"wav" -> "audio/wav"
|
|
||||||
"m4a" -> "audio/x-m4a"
|
|
||||||
"gif" -> "image/gif"
|
|
||||||
"jpeg", "jpg", "jfif", "pjpeg", "pjp" -> "image/jpeg"
|
|
||||||
"png" -> "image/png"
|
|
||||||
"apng" -> "image/apng"
|
|
||||||
"svg", "svgz" -> "image/svg+xml"
|
|
||||||
"webp" -> "image/webp"
|
|
||||||
"mht", "mhtml" -> "multipart/related"
|
|
||||||
"css" -> "text/css"
|
|
||||||
"html", "htm", "shtml", "shtm", "ehtml" -> "text/html"
|
|
||||||
"js", "mjs" -> "application/javascript"
|
|
||||||
"xml" -> "text/xml"
|
|
||||||
"mp4", "m4v" -> "video/mp4"
|
|
||||||
"ogv", "ogm" -> "video/ogg"
|
|
||||||
"ico" -> "image/x-icon"
|
|
||||||
"woff" -> "application/font-woff"
|
|
||||||
"gz", "tgz" -> "application/gzip"
|
|
||||||
"json" -> "application/json"
|
|
||||||
"pdf" -> "application/pdf"
|
|
||||||
"zip" -> "application/zip"
|
|
||||||
"bmp" -> "image/bmp"
|
|
||||||
"tiff", "tif" -> "image/tiff"
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,188 @@
|
||||||
|
package com.sukisu.ultra.ui.webui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.webkit.WebResourceResponse;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.WorkerThread;
|
||||||
|
import androidx.webkit.WebViewAssetLoader;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
import com.topjohnwu.superuser.io.SuFileInputStream;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler class to open files from file system by root access
|
||||||
|
* For more information about android storage please refer to
|
||||||
|
* <a href="https://developer.android.com/guide/topics/data/data-storage">Android Developers
|
||||||
|
* Docs: Data and file storage overview</a>.
|
||||||
|
* <p class="note">
|
||||||
|
* To avoid leaking user or app data to the web, make sure to choose {@code directory}
|
||||||
|
* carefully, and assume any file under this directory could be accessed by any web page subject
|
||||||
|
* to same-origin rules.
|
||||||
|
* <p>
|
||||||
|
* A typical usage would be like:
|
||||||
|
* <pre class="prettyprint">
|
||||||
|
* File publicDir = new File(context.getFilesDir(), "public");
|
||||||
|
* // Host "files/public/" in app's data directory under:
|
||||||
|
* // http://appassets.androidplatform.net/public/...
|
||||||
|
* WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
|
||||||
|
* .addPathHandler("/public/", new InternalStoragePathHandler(context, publicDir))
|
||||||
|
* .build();
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public final class SuFilePathHandler implements WebViewAssetLoader.PathHandler {
|
||||||
|
private static final String TAG = "SuFilePathHandler";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value to be used as MIME type if guessing MIME type failed.
|
||||||
|
*/
|
||||||
|
public static final String DEFAULT_MIME_TYPE = "text/plain";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forbidden subdirectories of {@link Context#getDataDir} that cannot be exposed by this
|
||||||
|
* handler. They are forbidden as they often contain sensitive information.
|
||||||
|
* <p class="note">
|
||||||
|
* Note: Any future addition to this list will be considered breaking changes to the API.
|
||||||
|
*/
|
||||||
|
private static final String[] FORBIDDEN_DATA_DIRS =
|
||||||
|
new String[] {"/data/data", "/data/system"};
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final File mDirectory;
|
||||||
|
|
||||||
|
private final Shell mShell;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates PathHandler for app's internal storage.
|
||||||
|
* The directory to be exposed must be inside either the application's internal data
|
||||||
|
* directory {@link Context#getDataDir} or cache directory {@link Context#getCacheDir}.
|
||||||
|
* External storage is not supported for security reasons, as other apps with
|
||||||
|
* {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} may be able to modify the
|
||||||
|
* files.
|
||||||
|
* <p>
|
||||||
|
* Exposing the entire data or cache directory is not permitted, to avoid accidentally
|
||||||
|
* exposing sensitive application files to the web. Certain existing subdirectories of
|
||||||
|
* {@link Context#getDataDir} are also not permitted as they are often sensitive.
|
||||||
|
* These files are ({@code "app_webview/"}, {@code "databases/"}, {@code "lib/"},
|
||||||
|
* {@code "shared_prefs/"} and {@code "code_cache/"}).
|
||||||
|
* <p>
|
||||||
|
* The application should typically use a dedicated subdirectory for the files it intends to
|
||||||
|
* expose and keep them separate from other files.
|
||||||
|
*
|
||||||
|
* @param directory the absolute path of the exposed app internal storage directory from
|
||||||
|
* which files can be loaded.
|
||||||
|
* @throws IllegalArgumentException if the directory is not allowed.
|
||||||
|
*/
|
||||||
|
public SuFilePathHandler(@NonNull File directory, Shell rootShell) {
|
||||||
|
try {
|
||||||
|
mDirectory = new File(getCanonicalDirPath(directory));
|
||||||
|
if (!isAllowedInternalStorageDir()) {
|
||||||
|
throw new IllegalArgumentException("The given directory \"" + directory
|
||||||
|
+ "\" doesn't exist under an allowed app internal storage directory");
|
||||||
|
}
|
||||||
|
mShell = rootShell;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Failed to resolve the canonical path for the given directory: "
|
||||||
|
+ directory.getPath(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAllowedInternalStorageDir() throws IOException {
|
||||||
|
String dir = getCanonicalDirPath(mDirectory);
|
||||||
|
|
||||||
|
for (String forbiddenPath : FORBIDDEN_DATA_DIRS) {
|
||||||
|
if (dir.startsWith(forbiddenPath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the requested file from the exposed data directory.
|
||||||
|
* <p>
|
||||||
|
* The matched prefix path used shouldn't be a prefix of a real web path. Thus, if the
|
||||||
|
* requested file cannot be found or is outside the mounted directory a
|
||||||
|
* {@link WebResourceResponse} object with a {@code null} {@link InputStream} will be
|
||||||
|
* returned instead of {@code null}. This saves the time of falling back to network and
|
||||||
|
* trying to resolve a path that doesn't exist. A {@link WebResourceResponse} with
|
||||||
|
* {@code null} {@link InputStream} will be received as an HTTP response with status code
|
||||||
|
* {@code 404} and no body.
|
||||||
|
* <p class="note">
|
||||||
|
* The MIME type for the file will be determined from the file's extension using
|
||||||
|
* {@link java.net.URLConnection#guessContentTypeFromName}. Developers should ensure that
|
||||||
|
* files are named using standard file extensions. If the file does not have a
|
||||||
|
* recognised extension, {@code "text/plain"} will be used by default.
|
||||||
|
*
|
||||||
|
* @param path the suffix path to be handled.
|
||||||
|
* @return {@link WebResourceResponse} for the requested file.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@WorkerThread
|
||||||
|
@NonNull
|
||||||
|
public WebResourceResponse handle(@NonNull String path) {
|
||||||
|
try {
|
||||||
|
File file = getCanonicalFileIfChild(mDirectory, path);
|
||||||
|
if (file != null) {
|
||||||
|
InputStream is = openFile(file, mShell);
|
||||||
|
String mimeType = guessMimeType(path);
|
||||||
|
return new WebResourceResponse(mimeType, null, is);
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, String.format(
|
||||||
|
"The requested file: %s is outside the mounted directory: %s", path,
|
||||||
|
mDirectory));
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Error opening the requested path: " + path, e);
|
||||||
|
}
|
||||||
|
return new WebResourceResponse(null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCanonicalDirPath(@NonNull File file) throws IOException {
|
||||||
|
String canonicalPath = file.getCanonicalPath();
|
||||||
|
if (!canonicalPath.endsWith("/")) canonicalPath += "/";
|
||||||
|
return canonicalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File getCanonicalFileIfChild(@NonNull File parent, @NonNull String child)
|
||||||
|
throws IOException {
|
||||||
|
String parentCanonicalPath = getCanonicalDirPath(parent);
|
||||||
|
String childCanonicalPath = new File(parent, child).getCanonicalPath();
|
||||||
|
if (childCanonicalPath.startsWith(parentCanonicalPath)) {
|
||||||
|
return new File(childCanonicalPath);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static InputStream handleSvgzStream(@NonNull String path,
|
||||||
|
@NonNull InputStream stream) throws IOException {
|
||||||
|
return path.endsWith(".svgz") ? new GZIPInputStream(stream) : stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InputStream openFile(@NonNull File file, @NonNull Shell shell) throws IOException {
|
||||||
|
SuFile suFile = new SuFile(file.getAbsolutePath());
|
||||||
|
suFile.setShell(shell);
|
||||||
|
InputStream fis = SuFileInputStream.open(suFile);
|
||||||
|
return handleSvgzStream(file.getPath(), fis);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use {@link MimeUtil#getMimeFromFileName} to guess MIME type or return the
|
||||||
|
* {@link #DEFAULT_MIME_TYPE} if it can't guess.
|
||||||
|
*
|
||||||
|
* @param filePath path of the file to guess its MIME type.
|
||||||
|
* @return MIME type guessed from file extension or {@link #DEFAULT_MIME_TYPE}.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public static String guessMimeType(@NonNull String filePath) {
|
||||||
|
String mimeType = MimeUtil.getMimeFromFileName(filePath);
|
||||||
|
return mimeType == null ? DEFAULT_MIME_TYPE : mimeType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,192 +0,0 @@
|
||||||
package com.sukisu.ultra.ui.webui
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.util.Log
|
|
||||||
import android.webkit.WebResourceResponse
|
|
||||||
import androidx.annotation.WorkerThread
|
|
||||||
import androidx.webkit.WebViewAssetLoader
|
|
||||||
import com.topjohnwu.superuser.Shell
|
|
||||||
import com.topjohnwu.superuser.io.SuFile
|
|
||||||
import com.topjohnwu.superuser.io.SuFileInputStream
|
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.io.File
|
|
||||||
import java.io.IOException
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
import java.util.zip.GZIPInputStream
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler class to open files from file system by root access
|
|
||||||
* For more information about android storage please refer to
|
|
||||||
* [Android Developers Docs: Data and file storage overview](https://developer.android.com/guide/topics/data/data-storage).
|
|
||||||
*
|
|
||||||
* To avoid leaking user or app data to the web, make sure to choose [directory]
|
|
||||||
* carefully, and assume any file under this directory could be accessed by any web page subject
|
|
||||||
* to same-origin rules.
|
|
||||||
*
|
|
||||||
* A typical usage would be like:
|
|
||||||
* ```
|
|
||||||
* val publicDir = File(context.filesDir, "public")
|
|
||||||
* // Host "files/public/" in app's data directory under:
|
|
||||||
* // http://appassets.androidplatform.net/public/...
|
|
||||||
* val assetLoader = WebViewAssetLoader.Builder()
|
|
||||||
* .addPathHandler("/public/", SuFilePathHandler(context, publicDir, shell, insetsSupplier))
|
|
||||||
* .build()
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
class SuFilePathHandler(
|
|
||||||
directory: File,
|
|
||||||
private val shell: Shell,
|
|
||||||
private val insetsSupplier: InsetsSupplier
|
|
||||||
) : WebViewAssetLoader.PathHandler {
|
|
||||||
|
|
||||||
private val directory: File
|
|
||||||
|
|
||||||
init {
|
|
||||||
try {
|
|
||||||
this.directory = File(getCanonicalDirPath(directory))
|
|
||||||
if (!isAllowedInternalStorageDir()) {
|
|
||||||
throw IllegalArgumentException(
|
|
||||||
"The given directory \"$directory\" doesn't exist under an allowed app internal storage directory"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} catch (e: IOException) {
|
|
||||||
throw IllegalArgumentException(
|
|
||||||
"Failed to resolve the canonical path for the given directory: ${directory.path}",
|
|
||||||
e
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun interface InsetsSupplier {
|
|
||||||
fun get(): Insets
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isAllowedInternalStorageDir(): Boolean {
|
|
||||||
return try {
|
|
||||||
val dir = getCanonicalDirPath(directory)
|
|
||||||
FORBIDDEN_DATA_DIRS.none { dir.startsWith(it) }
|
|
||||||
} catch (_: IOException) {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the requested file from the exposed data directory.
|
|
||||||
*
|
|
||||||
* The matched prefix path used shouldn't be a prefix of a real web path. Thus, if the
|
|
||||||
* requested file cannot be found or is outside the mounted directory a
|
|
||||||
* [WebResourceResponse] object with a `null` [InputStream] will be
|
|
||||||
* returned instead of `null`. This saves the time of falling back to network and
|
|
||||||
* trying to resolve a path that doesn't exist. A [WebResourceResponse] with
|
|
||||||
* `null` [InputStream] will be received as an HTTP response with status code
|
|
||||||
* `404` and no body.
|
|
||||||
*
|
|
||||||
* The MIME type for the file will be determined from the file's extension using
|
|
||||||
* [java.net.URLConnection.guessContentTypeFromName]. Developers should ensure that
|
|
||||||
* files are named using standard file extensions. If the file does not have a
|
|
||||||
* recognised extension, `"text/plain"` will be used by default.
|
|
||||||
*
|
|
||||||
* @param path the suffix path to be handled.
|
|
||||||
* @return [WebResourceResponse] for the requested file.
|
|
||||||
*/
|
|
||||||
@WorkerThread
|
|
||||||
override fun handle(path: String): WebResourceResponse {
|
|
||||||
if (path == "internal/insets.css") {
|
|
||||||
val css = insetsSupplier.get().css
|
|
||||||
return WebResourceResponse(
|
|
||||||
"text/css",
|
|
||||||
"utf-8",
|
|
||||||
ByteArrayInputStream(css.toByteArray(StandardCharsets.UTF_8))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
val file = getCanonicalFileIfChild(directory, path)
|
|
||||||
if (file != null) {
|
|
||||||
val inputStream = openFile(file, shell)
|
|
||||||
val mimeType = guessMimeType(path)
|
|
||||||
return WebResourceResponse(mimeType, null, inputStream)
|
|
||||||
} else {
|
|
||||||
Log.e(
|
|
||||||
TAG,
|
|
||||||
"The requested file: $path is outside the mounted directory: $directory"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Log.e(TAG, "Error opening the requested path: $path", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
return WebResourceResponse(null, null, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val TAG = "SuFilePathHandler"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default value to be used as MIME type if guessing MIME type failed.
|
|
||||||
*/
|
|
||||||
const val DEFAULT_MIME_TYPE = "text/plain"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Forbidden subdirectories of [Context.getDataDir] that cannot be exposed by this
|
|
||||||
* handler. They are forbidden as they often contain sensitive information.
|
|
||||||
*
|
|
||||||
* Note: Any future addition to this list will be considered breaking changes to the API.
|
|
||||||
*/
|
|
||||||
private val FORBIDDEN_DATA_DIRS = arrayOf("/data/data", "/data/system")
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@Throws(IOException::class)
|
|
||||||
fun getCanonicalDirPath(file: File): String {
|
|
||||||
var canonicalPath = file.canonicalPath
|
|
||||||
if (!canonicalPath.endsWith("/")) {
|
|
||||||
canonicalPath += "/"
|
|
||||||
}
|
|
||||||
return canonicalPath
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@Throws(IOException::class)
|
|
||||||
fun getCanonicalFileIfChild(parent: File, child: String): File? {
|
|
||||||
val parentCanonicalPath = getCanonicalDirPath(parent)
|
|
||||||
val childCanonicalPath = File(parent, child).canonicalPath
|
|
||||||
return if (childCanonicalPath.startsWith(parentCanonicalPath)) {
|
|
||||||
File(childCanonicalPath)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
|
||||||
private fun handleSvgzStream(path: String, stream: InputStream): InputStream {
|
|
||||||
return if (path.endsWith(".svgz")) {
|
|
||||||
GZIPInputStream(stream)
|
|
||||||
} else {
|
|
||||||
stream
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@Throws(IOException::class)
|
|
||||||
fun openFile(file: File, shell: Shell): InputStream {
|
|
||||||
val suFile = SuFile(file.absolutePath).apply {
|
|
||||||
setShell(shell)
|
|
||||||
}
|
|
||||||
val fis = SuFileInputStream.open(suFile)
|
|
||||||
return handleSvgzStream(file.path, fis)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use [MimeUtil.getMimeFromFileName] to guess MIME type or return the
|
|
||||||
* [DEFAULT_MIME_TYPE] if it can't guess.
|
|
||||||
*
|
|
||||||
* @param filePath path of the file to guess its MIME type.
|
|
||||||
* @return MIME type guessed from file extension or [DEFAULT_MIME_TYPE].
|
|
||||||
*/
|
|
||||||
@JvmStatic
|
|
||||||
fun guessMimeType(filePath: String): String {
|
|
||||||
return MimeUtil.getMimeFromFileName(filePath) ?: DEFAULT_MIME_TYPE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,38 +2,32 @@ package com.sukisu.ultra.ui.webui
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.ActivityManager
|
import android.app.ActivityManager
|
||||||
import android.graphics.Color
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.ViewGroup.MarginLayoutParams
|
||||||
import android.webkit.WebResourceRequest
|
import android.webkit.WebResourceRequest
|
||||||
import android.webkit.WebResourceResponse
|
import android.webkit.WebResourceResponse
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import android.webkit.WebViewClient
|
import android.webkit.WebViewClient
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.activity.viewModels
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.webkit.WebViewAssetLoader
|
import androidx.webkit.WebViewAssetLoader
|
||||||
import com.dergoogler.mmrl.platform.model.ModId
|
import com.dergoogler.mmrl.platform.model.ModId
|
||||||
import com.dergoogler.mmrl.webui.interfaces.WXOptions
|
import com.dergoogler.mmrl.webui.interfaces.WXOptions
|
||||||
import com.sukisu.ultra.ui.util.createRootShell
|
import com.sukisu.ultra.ui.util.createRootShell
|
||||||
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
|
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
class WebUIActivity : ComponentActivity() {
|
class WebUIActivity : ComponentActivity() {
|
||||||
private val rootShell by lazy { createRootShell(true) }
|
private val rootShell by lazy { createRootShell(true) }
|
||||||
|
private val superUserViewModel: SuperUserViewModel by viewModels()
|
||||||
private lateinit var insets: Insets
|
|
||||||
private var webView = null as WebView?
|
private var webView = null as WebView?
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
|
@ -46,21 +40,10 @@ class WebUIActivity : ComponentActivity() {
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setContent {
|
lifecycleScope.launch {
|
||||||
Box(
|
superUserViewModel.fetchAppList()
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
CircularProgressIndicator()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lifecycleScope.launch {
|
|
||||||
SuperUserViewModel.isAppListLoaded.first { it }
|
|
||||||
setupWebView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private fun setupWebView() {
|
|
||||||
val moduleId = intent.getStringExtra("id") ?: finishAndRemoveTask().let { return }
|
val moduleId = intent.getStringExtra("id") ?: finishAndRemoveTask().let { return }
|
||||||
val name = intent.getStringExtra("name") ?: finishAndRemoveTask().let { return }
|
val name = intent.getStringExtra("name") ?: finishAndRemoveTask().let { return }
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
|
@ -77,12 +60,11 @@ class WebUIActivity : ComponentActivity() {
|
||||||
|
|
||||||
val moduleDir = "/data/adb/modules/${moduleId}"
|
val moduleDir = "/data/adb/modules/${moduleId}"
|
||||||
val webRoot = File("${moduleDir}/webroot")
|
val webRoot = File("${moduleDir}/webroot")
|
||||||
insets = Insets(0, 0, 0, 0)
|
|
||||||
val webViewAssetLoader = WebViewAssetLoader.Builder()
|
val webViewAssetLoader = WebViewAssetLoader.Builder()
|
||||||
.setDomain("mui.kernelsu.org")
|
.setDomain("mui.kernelsu.org")
|
||||||
.addPathHandler(
|
.addPathHandler(
|
||||||
"/",
|
"/",
|
||||||
SuFilePathHandler(webRoot, rootShell) { insets }
|
SuFilePathHandler(webRoot, rootShell)
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
|
@ -112,18 +94,15 @@ class WebUIActivity : ComponentActivity() {
|
||||||
val webView = WebView(this).apply {
|
val webView = WebView(this).apply {
|
||||||
webView = this
|
webView = this
|
||||||
|
|
||||||
setBackgroundColor(Color.TRANSPARENT)
|
ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
|
||||||
val density = resources.displayMetrics.density
|
val inset = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||||
|
view.updateLayoutParams<MarginLayoutParams> {
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(this) { _, windowInsets ->
|
leftMargin = inset.left
|
||||||
val inset = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout())
|
rightMargin = inset.right
|
||||||
insets = Insets(
|
topMargin = inset.top
|
||||||
top = (inset.top / density).toInt(),
|
bottomMargin = inset.bottom
|
||||||
bottom = (inset.bottom / density).toInt(),
|
}
|
||||||
left = (inset.left / density).toInt(),
|
return@setOnApplyWindowInsetsListener insets
|
||||||
right = (inset.right / density).toInt()
|
|
||||||
)
|
|
||||||
WindowInsetsCompat.CONSUMED
|
|
||||||
}
|
}
|
||||||
settings.javaScriptEnabled = true
|
settings.javaScriptEnabled = true
|
||||||
settings.domStorageEnabled = true
|
settings.domStorageEnabled = true
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,6 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.*
|
import androidx.compose.material.icons.filled.*
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
|
@ -28,7 +25,6 @@ import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.content.edit
|
|
||||||
import com.ramcosta.composedestinations.annotation.Destination
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
|
|
@ -37,8 +33,8 @@ import com.sukisu.ultra.Natives
|
||||||
import com.sukisu.ultra.R
|
import com.sukisu.ultra.R
|
||||||
import com.sukisu.ultra.ui.theme.component.ImageEditorDialog
|
import com.sukisu.ultra.ui.theme.component.ImageEditorDialog
|
||||||
import com.sukisu.ultra.ui.component.KsuIsValid
|
import com.sukisu.ultra.ui.component.KsuIsValid
|
||||||
import com.sukisu.ultra.ui.screen.SwitchItem
|
|
||||||
import com.sukisu.ultra.ui.theme.*
|
import com.sukisu.ultra.ui.theme.*
|
||||||
|
import com.sukisu.ultra.ui.util.*
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -49,7 +45,6 @@ import zako.zako.zako.zakoui.screen.moreSettings.component.SettingItem
|
||||||
import zako.zako.zako.zakoui.screen.moreSettings.component.SettingsCard
|
import zako.zako.zako.zakoui.screen.moreSettings.component.SettingsCard
|
||||||
import zako.zako.zako.zakoui.screen.moreSettings.component.SettingsDivider
|
import zako.zako.zako.zakoui.screen.moreSettings.component.SettingsDivider
|
||||||
import zako.zako.zako.zakoui.screen.moreSettings.component.SwitchSettingItem
|
import zako.zako.zako.zakoui.screen.moreSettings.component.SwitchSettingItem
|
||||||
import zako.zako.zako.zakoui.screen.moreSettings.component.UidScannerSection
|
|
||||||
import zako.zako.zako.zakoui.screen.moreSettings.state.MoreSettingsState
|
import zako.zako.zako.zakoui.screen.moreSettings.state.MoreSettingsState
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
|
@ -346,11 +341,6 @@ private fun AdvancedSettings(
|
||||||
state: MoreSettingsState,
|
state: MoreSettingsState,
|
||||||
handlers: MoreSettingsHandlers
|
handlers: MoreSettingsHandlers
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
val snackBarHost = remember { SnackbarHostState() }
|
|
||||||
val prefs = remember { context.getSharedPreferences("settings", Context.MODE_PRIVATE) }
|
|
||||||
|
|
||||||
SettingsCard(title = stringResource(R.string.advanced_settings)) {
|
SettingsCard(title = stringResource(R.string.advanced_settings)) {
|
||||||
// SELinux 开关
|
// SELinux 开关
|
||||||
SwitchSettingItem(
|
SwitchSettingItem(
|
||||||
|
|
@ -363,27 +353,6 @@ private fun AdvancedSettings(
|
||||||
onChange = handlers::handleSelinuxChange
|
onChange = handlers::handleSelinuxChange
|
||||||
)
|
)
|
||||||
|
|
||||||
var forceSignatureVerification by rememberSaveable {
|
|
||||||
mutableStateOf(prefs.getBoolean("force_signature_verification", false))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 强制签名验证开关
|
|
||||||
SwitchItem(
|
|
||||||
icon = Icons.Filled.Security,
|
|
||||||
title = stringResource(R.string.module_signature_verification),
|
|
||||||
summary = stringResource(R.string.module_signature_verification_summary),
|
|
||||||
checked = forceSignatureVerification,
|
|
||||||
onCheckedChange = { enabled ->
|
|
||||||
prefs.edit { putBoolean("force_signature_verification", enabled) }
|
|
||||||
forceSignatureVerification = enabled
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// UID 扫描开关
|
|
||||||
if (Natives.version >= Natives.MINIMAL_SUPPORTED_UID_SCANNER && Natives.version >= Natives.MINIMAL_NEW_IOCTL_KERNEL) {
|
|
||||||
UidScannerSection(prefs, snackBarHost, scope, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 动态管理器设置
|
// 动态管理器设置
|
||||||
if (Natives.version >= Natives.MINIMAL_SUPPORTED_DYNAMIC_MANAGER && Natives.version >= Natives.MINIMAL_NEW_IOCTL_KERNEL) {
|
if (Natives.version >= Natives.MINIMAL_SUPPORTED_DYNAMIC_MANAGER && Natives.version >= Natives.MINIMAL_NEW_IOCTL_KERNEL) {
|
||||||
SettingItem(
|
SettingItem(
|
||||||
|
|
|
||||||
|
|
@ -6,38 +6,15 @@ import android.content.SharedPreferences
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
|
||||||
import androidx.compose.animation.expandVertically
|
|
||||||
import androidx.compose.animation.fadeIn
|
|
||||||
import androidx.compose.animation.fadeOut
|
|
||||||
import androidx.compose.animation.shrinkVertically
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.CleaningServices
|
|
||||||
import androidx.compose.material.icons.filled.Groups
|
|
||||||
import androidx.compose.material.icons.filled.Scanner
|
|
||||||
import androidx.compose.material3.SnackbarHostState
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import com.sukisu.ultra.Natives
|
import com.sukisu.ultra.Natives
|
||||||
import com.sukisu.ultra.R
|
import com.sukisu.ultra.R
|
||||||
import com.sukisu.ultra.ui.component.ConfirmResult
|
|
||||||
import com.sukisu.ultra.ui.component.rememberConfirmDialog
|
|
||||||
import com.sukisu.ultra.ui.screen.SettingItem
|
|
||||||
import com.sukisu.ultra.ui.screen.SwitchItem
|
|
||||||
import com.sukisu.ultra.ui.theme.*
|
import com.sukisu.ultra.ui.theme.*
|
||||||
import com.sukisu.ultra.ui.util.*
|
import com.sukisu.ultra.ui.util.*
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import zako.zako.zako.zakoui.screen.moreSettings.state.MoreSettingsState
|
import zako.zako.zako.zakoui.screen.moreSettings.state.MoreSettingsState
|
||||||
import zako.zako.zako.zakoui.screen.moreSettings.util.toggleLauncherIcon
|
import zako.zako.zako.zakoui.screen.moreSettings.util.toggleLauncherIcon
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,13 @@
|
||||||
package zako.zako.zako.zakoui.screen.moreSettings.component
|
package zako.zako.zako.zakoui.screen.moreSettings.component
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
|
||||||
import androidx.compose.animation.expandVertically
|
|
||||||
import androidx.compose.animation.fadeIn
|
|
||||||
import androidx.compose.animation.fadeOut
|
|
||||||
import androidx.compose.animation.shrinkVertically
|
|
||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.*
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Check
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material.icons.filled.CleaningServices
|
|
||||||
import androidx.compose.material.icons.filled.Groups
|
|
||||||
import androidx.compose.material.icons.filled.Scanner
|
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
|
@ -30,22 +20,9 @@ import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
|
||||||
import com.maxkeppeler.sheets.list.ListDialog
|
import com.maxkeppeler.sheets.list.ListDialog
|
||||||
import com.maxkeppeler.sheets.list.models.ListOption
|
import com.maxkeppeler.sheets.list.models.ListOption
|
||||||
import com.maxkeppeler.sheets.list.models.ListSelection
|
import com.maxkeppeler.sheets.list.models.ListSelection
|
||||||
import com.sukisu.ultra.Natives
|
|
||||||
import zako.zako.zako.zakoui.screen.moreSettings.util.LocaleHelper
|
import zako.zako.zako.zakoui.screen.moreSettings.util.LocaleHelper
|
||||||
import com.sukisu.ultra.R
|
import com.sukisu.ultra.R
|
||||||
import com.sukisu.ultra.ui.component.ConfirmResult
|
|
||||||
import com.sukisu.ultra.ui.component.rememberConfirmDialog
|
|
||||||
import com.sukisu.ultra.ui.screen.SwitchItem
|
|
||||||
import com.sukisu.ultra.ui.theme.*
|
import com.sukisu.ultra.ui.theme.*
|
||||||
import com.sukisu.ultra.ui.util.cleanRuntimeEnvironment
|
|
||||||
import com.sukisu.ultra.ui.util.getUidMultiUserScan
|
|
||||||
import com.sukisu.ultra.ui.util.readUidScannerFile
|
|
||||||
import com.sukisu.ultra.ui.util.setUidAutoScan
|
|
||||||
import com.sukisu.ultra.ui.util.setUidMultiUserScan
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import zako.zako.zako.zakoui.screen.moreSettings.MoreSettingsHandlers
|
import zako.zako.zako.zakoui.screen.moreSettings.MoreSettingsHandlers
|
||||||
import zako.zako.zako.zakoui.screen.moreSettings.state.MoreSettingsState
|
import zako.zako.zako.zakoui.screen.moreSettings.state.MoreSettingsState
|
||||||
|
|
||||||
|
|
@ -496,125 +473,4 @@ fun DynamicManagerDialog(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun UidScannerSection(
|
|
||||||
prefs: SharedPreferences,
|
|
||||||
snackBarHost: SnackbarHostState,
|
|
||||||
scope: CoroutineScope,
|
|
||||||
context: Context
|
|
||||||
) {
|
|
||||||
if (Natives.version < Natives.MINIMAL_SUPPORTED_UID_SCANNER) return
|
|
||||||
|
|
||||||
val realAuto = Natives.isUidScannerEnabled()
|
|
||||||
val realMulti = getUidMultiUserScan()
|
|
||||||
|
|
||||||
var autoOn by remember { mutableStateOf(realAuto) }
|
|
||||||
var multiOn by remember { mutableStateOf(realMulti) }
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
autoOn = realAuto
|
|
||||||
multiOn = realMulti
|
|
||||||
prefs.edit {
|
|
||||||
putBoolean("uid_auto_scan", autoOn)
|
|
||||||
putBoolean("uid_multi_user_scan", multiOn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SwitchItem(
|
|
||||||
icon = Icons.Filled.Scanner,
|
|
||||||
title = stringResource(R.string.uid_auto_scan_title),
|
|
||||||
summary = stringResource(R.string.uid_auto_scan_summary),
|
|
||||||
checked = autoOn,
|
|
||||||
onCheckedChange = { target ->
|
|
||||||
autoOn = target
|
|
||||||
if (!target) multiOn = false
|
|
||||||
|
|
||||||
scope.launch(Dispatchers.IO) {
|
|
||||||
setUidAutoScan(target)
|
|
||||||
val actual = Natives.isUidScannerEnabled() || readUidScannerFile()
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
autoOn = actual
|
|
||||||
if (!actual) multiOn = false
|
|
||||||
prefs.edit {
|
|
||||||
putBoolean("uid_auto_scan", actual)
|
|
||||||
putBoolean("uid_multi_user_scan", multiOn)
|
|
||||||
}
|
|
||||||
if (actual != target) {
|
|
||||||
snackBarHost.showSnackbar(
|
|
||||||
context.getString(R.string.uid_scanner_setting_failed)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
AnimatedVisibility(
|
|
||||||
visible = autoOn,
|
|
||||||
enter = fadeIn() + expandVertically(),
|
|
||||||
exit = fadeOut() + shrinkVertically()
|
|
||||||
) {
|
|
||||||
SwitchItem(
|
|
||||||
icon = Icons.Filled.Groups,
|
|
||||||
title = stringResource(R.string.uid_multi_user_scan_title),
|
|
||||||
summary = stringResource(R.string.uid_multi_user_scan_summary),
|
|
||||||
checked = multiOn,
|
|
||||||
onCheckedChange = { target ->
|
|
||||||
scope.launch(Dispatchers.IO) {
|
|
||||||
val ok = setUidMultiUserScan(target)
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
if (ok) {
|
|
||||||
multiOn = target
|
|
||||||
prefs.edit { putBoolean("uid_multi_user_scan", target) }
|
|
||||||
} else {
|
|
||||||
snackBarHost.showSnackbar(
|
|
||||||
context.getString(R.string.uid_scanner_setting_failed)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimatedVisibility(
|
|
||||||
visible = autoOn,
|
|
||||||
enter = fadeIn() + expandVertically(),
|
|
||||||
exit = fadeOut() + shrinkVertically()
|
|
||||||
) {
|
|
||||||
val confirmDialog = rememberConfirmDialog()
|
|
||||||
com.sukisu.ultra.ui.screen.SettingItem(
|
|
||||||
icon = Icons.Filled.CleaningServices,
|
|
||||||
title = stringResource(R.string.clean_runtime_environment),
|
|
||||||
summary = stringResource(R.string.clean_runtime_environment_summary),
|
|
||||||
onClick = {
|
|
||||||
scope.launch {
|
|
||||||
if (confirmDialog.awaitConfirm(
|
|
||||||
title = context.getString(R.string.clean_runtime_environment),
|
|
||||||
content = context.getString(R.string.clean_runtime_environment_confirm)
|
|
||||||
) == ConfirmResult.Confirmed
|
|
||||||
) {
|
|
||||||
if (cleanRuntimeEnvironment()) {
|
|
||||||
autoOn = false
|
|
||||||
multiOn = false
|
|
||||||
prefs.edit {
|
|
||||||
putBoolean("uid_auto_scan", false)
|
|
||||||
putBoolean("uid_multi_user_scan", false)
|
|
||||||
}
|
|
||||||
Natives.setUidScannerEnabled(false)
|
|
||||||
snackBarHost.showSnackbar(
|
|
||||||
context.getString(R.string.clean_runtime_environment_success)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
snackBarHost.showSnackbar(
|
|
||||||
context.getString(R.string.clean_runtime_environment_failed)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -70,6 +70,9 @@ class MoreSettingsState(
|
||||||
// SELinux状态
|
// SELinux状态
|
||||||
var selinuxEnabled by mutableStateOf(false)
|
var selinuxEnabled by mutableStateOf(false)
|
||||||
|
|
||||||
|
// SuSFS 状态
|
||||||
|
var isSusFSEnabled by mutableStateOf(true)
|
||||||
|
|
||||||
// 卡片配置状态
|
// 卡片配置状态
|
||||||
var cardAlpha by mutableFloatStateOf(CardConfig.cardAlpha)
|
var cardAlpha by mutableFloatStateOf(CardConfig.cardAlpha)
|
||||||
var cardDim by mutableFloatStateOf(CardConfig.cardDim)
|
var cardDim by mutableFloatStateOf(CardConfig.cardDim)
|
||||||
|
|
|
||||||
Binary file not shown.
BIN
manager/app/src/main/jniLibs/arm64-v8a/libzakosign.so
Normal file
BIN
manager/app/src/main/jniLibs/arm64-v8a/libzakosign.so
Normal file
Binary file not shown.
BIN
manager/app/src/main/jniLibs/armeabi-v7a/libzakosign.so
Normal file
BIN
manager/app/src/main/jniLibs/armeabi-v7a/libzakosign.so
Normal file
Binary file not shown.
|
|
@ -126,6 +126,7 @@
|
||||||
<string name="selected_lkm">LKM المحددة: %s</string>
|
<string name="selected_lkm">LKM المحددة: %s</string>
|
||||||
<string name="save_log">حفظ السجلات</string>
|
<string name="save_log">حفظ السجلات</string>
|
||||||
<string name="log_saved">السجلات محفوظة</string>
|
<string name="log_saved">السجلات محفوظة</string>
|
||||||
|
<string name="sus_su_mode">وضع SuS SU</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">تأكيد وحدة التثبيت %1$s؟</string>
|
<string name="module_install_confirm">تأكيد وحدة التثبيت %1$s؟</string>
|
||||||
<string name="unknown_module">وحدة غير معروفة</string>
|
<string name="unknown_module">وحدة غير معروفة</string>
|
||||||
|
|
@ -275,6 +276,8 @@
|
||||||
<string name="advanced_settings">إعدادات متقدمة</string>
|
<string name="advanced_settings">إعدادات متقدمة</string>
|
||||||
<string name="appearance_settings">تخصيص شريط الأدوات</string>
|
<string name="appearance_settings">تخصيص شريط الأدوات</string>
|
||||||
<string name="back">عد مرة أخرى</string>
|
<string name="back">عد مرة أخرى</string>
|
||||||
|
<string name="susfs_enabled">تم تمكين SuSFS</string>
|
||||||
|
<string name="susfs_disabled">تم تعطيل SuSFS</string>
|
||||||
<string name="background_set_success">تم تعيين الخلفية بنجاح</string>
|
<string name="background_set_success">تم تعيين الخلفية بنجاح</string>
|
||||||
<string name="background_removed">إزالة خلفيات مخصصة</string>
|
<string name="background_removed">إزالة خلفيات مخصصة</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">Selected LKM: %s</string>
|
<string name="selected_lkm">Selected LKM: %s</string>
|
||||||
<string name="save_log">Girişləri Saxla</string>
|
<string name="save_log">Girişləri Saxla</string>
|
||||||
<string name="log_saved">Logs saved</string>
|
<string name="log_saved">Logs saved</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -273,6 +274,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">Selected LKM: %s</string>
|
<string name="selected_lkm">Selected LKM: %s</string>
|
||||||
<string name="save_log">Sačuvaj Dnevnike</string>
|
<string name="save_log">Sačuvaj Dnevnike</string>
|
||||||
<string name="log_saved">Logs saved</string>
|
<string name="log_saved">Logs saved</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -273,6 +274,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">Selected LKM: %s</string>
|
<string name="selected_lkm">Selected LKM: %s</string>
|
||||||
<string name="save_log">Gem Logfiler</string>
|
<string name="save_log">Gem Logfiler</string>
|
||||||
<string name="log_saved">Logs saved</string>
|
<string name="log_saved">Logs saved</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -273,6 +274,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@
|
||||||
<string name="selected_lkm">Wähle LKM: %s</string>
|
<string name="selected_lkm">Wähle LKM: %s</string>
|
||||||
<string name="save_log">Protokolle Speichern</string>
|
<string name="save_log">Protokolle Speichern</string>
|
||||||
<string name="log_saved">Protokolle gespeichert</string>
|
<string name="log_saved">Protokolle gespeichert</string>
|
||||||
|
<string name="sus_su_mode">SuS SU-Modus:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">das Installationsmodul %1$s bestätigen ?</string>
|
<string name="module_install_confirm">das Installationsmodul %1$s bestätigen ?</string>
|
||||||
<string name="unknown_module">unbekannter Modul</string>
|
<string name="unknown_module">unbekannter Modul</string>
|
||||||
|
|
@ -275,6 +276,8 @@
|
||||||
<string name="advanced_settings">Erweiterte Einstellungen</string>
|
<string name="advanced_settings">Erweiterte Einstellungen</string>
|
||||||
<string name="appearance_settings">Passt die Symbolleiste an.</string>
|
<string name="appearance_settings">Passt die Symbolleiste an.</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS aktiviert</string>
|
||||||
|
<string name="susfs_disabled">SuSFS deaktiviert</string>
|
||||||
<string name="background_set_success">Hintergrund erfolgreich gesetzt</string>
|
<string name="background_set_success">Hintergrund erfolgreich gesetzt</string>
|
||||||
<string name="background_removed">Eigene Hintergründe entfernt</string>
|
<string name="background_removed">Eigene Hintergründe entfernt</string>
|
||||||
<string name="icon_switch_title">Alternatives Symbol</string>
|
<string name="icon_switch_title">Alternatives Symbol</string>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">LKM seleccionado: %s</string>
|
<string name="selected_lkm">LKM seleccionado: %s</string>
|
||||||
<string name="save_log">Guardar registros</string>
|
<string name="save_log">Guardar registros</string>
|
||||||
<string name="log_saved">Registro guardado</string>
|
<string name="log_saved">Registro guardado</string>
|
||||||
|
<string name="sus_su_mode">Modo SuS SU:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">¿confirmar la instalación del módulo %1$s?</string>
|
<string name="module_install_confirm">¿confirmar la instalación del módulo %1$s?</string>
|
||||||
<string name="unknown_module">módulo desconocido</string>
|
<string name="unknown_module">módulo desconocido</string>
|
||||||
|
|
@ -273,6 +274,8 @@
|
||||||
<string name="advanced_settings">Configuraciones avanzadas</string>
|
<string name="advanced_settings">Configuraciones avanzadas</string>
|
||||||
<string name="appearance_settings">Personalizar la barra de herramientas.</string>
|
<string name="appearance_settings">Personalizar la barra de herramientas.</string>
|
||||||
<string name="back">Retorno</string>
|
<string name="back">Retorno</string>
|
||||||
|
<string name="susfs_enabled">SuSFS activado</string>
|
||||||
|
<string name="susfs_disabled">SuSFS desactivado</string>
|
||||||
<string name="background_set_success">Fondo establecido correctamente</string>
|
<string name="background_set_success">Fondo establecido correctamente</string>
|
||||||
<string name="background_removed">Eliminar fondo personalizado</string>
|
<string name="background_removed">Eliminar fondo personalizado</string>
|
||||||
<string name="icon_switch_title">Icono alternativo</string>
|
<string name="icon_switch_title">Icono alternativo</string>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">Valitud LKM: %s</string>
|
<string name="selected_lkm">Valitud LKM: %s</string>
|
||||||
<string name="save_log">Salvesta Logid</string>
|
<string name="save_log">Salvesta Logid</string>
|
||||||
<string name="log_saved">Logs saved</string>
|
<string name="log_saved">Logs saved</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -273,6 +274,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">Selected LKM: %s</string>
|
<string name="selected_lkm">Selected LKM: %s</string>
|
||||||
<string name="save_log">ذخیره گزارشها</string>
|
<string name="save_log">ذخیره گزارشها</string>
|
||||||
<string name="log_saved">Logs saved</string>
|
<string name="log_saved">Logs saved</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -273,6 +274,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">Selected LKM: %s</string>
|
<string name="selected_lkm">Selected LKM: %s</string>
|
||||||
<string name="save_log">I-save ang mga Log</string>
|
<string name="save_log">I-save ang mga Log</string>
|
||||||
<string name="log_saved">Logs saved</string>
|
<string name="log_saved">Logs saved</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -273,6 +274,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@
|
||||||
<string name="selected_lkm">LKM sélectionné : %s</string>
|
<string name="selected_lkm">LKM sélectionné : %s</string>
|
||||||
<string name="save_log">Enregistrer les journaux</string>
|
<string name="save_log">Enregistrer les journaux</string>
|
||||||
<string name="log_saved">Journaux enregistrés</string>
|
<string name="log_saved">Journaux enregistrés</string>
|
||||||
|
<string name="sus_su_mode">Mode Sus</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirmer l\'installation du module %1$s?</string>
|
<string name="module_install_confirm">confirmer l\'installation du module %1$s?</string>
|
||||||
<string name="unknown_module">module inconnu</string>
|
<string name="unknown_module">module inconnu</string>
|
||||||
|
|
@ -275,6 +276,8 @@
|
||||||
<string name="advanced_settings">Paramètres avancés</string>
|
<string name="advanced_settings">Paramètres avancés</string>
|
||||||
<string name="appearance_settings">Choisir les boutons à afficher</string>
|
<string name="appearance_settings">Choisir les boutons à afficher</string>
|
||||||
<string name="back">Reviens</string>
|
<string name="back">Reviens</string>
|
||||||
|
<string name="susfs_enabled">SuSFS activé</string>
|
||||||
|
<string name="susfs_disabled">SuSFS désactivé</string>
|
||||||
<string name="background_set_success">Fond d\'écran défini avec succès</string>
|
<string name="background_set_success">Fond d\'écran défini avec succès</string>
|
||||||
<string name="background_removed">Fond d\'écran personnalisé supprimé</string>
|
<string name="background_removed">Fond d\'écran personnalisé supprimé</string>
|
||||||
<string name="icon_switch_title">Icône alternative</string>
|
<string name="icon_switch_title">Icône alternative</string>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">Selected LKM: %s</string>
|
<string name="selected_lkm">Selected LKM: %s</string>
|
||||||
<string name="save_log">लॉग सहेजें</string>
|
<string name="save_log">लॉग सहेजें</string>
|
||||||
<string name="log_saved">Logs saved</string>
|
<string name="log_saved">Logs saved</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -273,6 +274,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">Selected LKM: %s</string>
|
<string name="selected_lkm">Selected LKM: %s</string>
|
||||||
<string name="save_log">Spremi Zapise</string>
|
<string name="save_log">Spremi Zapise</string>
|
||||||
<string name="log_saved">Logs saved</string>
|
<string name="log_saved">Logs saved</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -273,6 +274,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">Kiválasztott LKM: %s</string>
|
<string name="selected_lkm">Kiválasztott LKM: %s</string>
|
||||||
<string name="save_log">Naplók mentése</string>
|
<string name="save_log">Naplók mentése</string>
|
||||||
<string name="log_saved">Mentett naplók</string>
|
<string name="log_saved">Mentett naplók</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -273,6 +274,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,7 @@ Gunakan opsi ini hanya setelah OTA selesai.
|
||||||
Lanjutkan?</string>
|
Lanjutkan?</string>
|
||||||
<string name="install_next">Lanjut</string>
|
<string name="install_next">Lanjut</string>
|
||||||
<string name="select_file_tip">Disarankan gambar partisi %1$s</string>
|
<string name="select_file_tip">Disarankan gambar partisi %1$s</string>
|
||||||
|
<string name="select_file_tip_vendor">(tidak stabil)</string>
|
||||||
<string name="select_kmi">Pilih KMI</string>
|
<string name="select_kmi">Pilih KMI</string>
|
||||||
<string name="settings_uninstall">Copot Pemasangan</string>
|
<string name="settings_uninstall">Copot Pemasangan</string>
|
||||||
<string name="settings_uninstall_temporary">Copot Pemasangan Sementara</string>
|
<string name="settings_uninstall_temporary">Copot Pemasangan Sementara</string>
|
||||||
|
|
@ -127,6 +128,7 @@ Lanjutkan?</string>
|
||||||
<string name="selected_lkm">LKM Terpilih: %s</string>
|
<string name="selected_lkm">LKM Terpilih: %s</string>
|
||||||
<string name="save_log">Simpan Log</string>
|
<string name="save_log">Simpan Log</string>
|
||||||
<string name="log_saved">Log Disimpan</string>
|
<string name="log_saved">Log Disimpan</string>
|
||||||
|
<string name="sus_su_mode">Mode SuS SU:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">Konfirmasi pemasangan modul %1$s?</string>
|
<string name="module_install_confirm">Konfirmasi pemasangan modul %1$s?</string>
|
||||||
<string name="unknown_module">modul tidak dikenal</string>
|
<string name="unknown_module">modul tidak dikenal</string>
|
||||||
|
|
@ -281,6 +283,8 @@ Tanamkan: Secara permanen memasang ke sistem</string>
|
||||||
<string name="advanced_settings">Pengaturan Lanjutan</string>
|
<string name="advanced_settings">Pengaturan Lanjutan</string>
|
||||||
<string name="appearance_settings">Sesuaikan Bilah Alat</string>
|
<string name="appearance_settings">Sesuaikan Bilah Alat</string>
|
||||||
<string name="back">Kembali</string>
|
<string name="back">Kembali</string>
|
||||||
|
<string name="susfs_enabled">SuSFS diaktifkan</string>
|
||||||
|
<string name="susfs_disabled">SuSFS dinonaktifkan</string>
|
||||||
<string name="background_set_success">Latar belakang berhasil diatur</string>
|
<string name="background_set_success">Latar belakang berhasil diatur</string>
|
||||||
<string name="background_removed">Latar belakang khusus dihapus</string>
|
<string name="background_removed">Latar belakang khusus dihapus</string>
|
||||||
<string name="icon_switch_title">Ikon Alternatif</string>
|
<string name="icon_switch_title">Ikon Alternatif</string>
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@
|
||||||
<string name="install_upload_lkm_file">Gunakan berkas LKM lokal</string>
|
<string name="install_upload_lkm_file">Gunakan berkas LKM lokal</string>
|
||||||
<string name="install_only_support_ko_file">Hanya berkas .ko yang didukung</string>
|
<string name="install_only_support_ko_file">Hanya berkas .ko yang didukung</string>
|
||||||
<string name="select_file_tip">%1$s image partisi terekomendasi</string>
|
<string name="select_file_tip">%1$s image partisi terekomendasi</string>
|
||||||
|
<string name="select_file_tip_vendor">(tidak stabil)</string>
|
||||||
<string name="select_kmi">Pilih KMI</string>
|
<string name="select_kmi">Pilih KMI</string>
|
||||||
<string name="settings_uninstall">Hapus</string>
|
<string name="settings_uninstall">Hapus</string>
|
||||||
<string name="settings_uninstall_temporary">Hapus sementara</string>
|
<string name="settings_uninstall_temporary">Hapus sementara</string>
|
||||||
|
|
@ -128,6 +129,7 @@
|
||||||
<string name="selected_lkm">LKM dipilih: %s</string>
|
<string name="selected_lkm">LKM dipilih: %s</string>
|
||||||
<string name="save_log">Simpan Log</string>
|
<string name="save_log">Simpan Log</string>
|
||||||
<string name="log_saved">Log disimpan</string>
|
<string name="log_saved">Log disimpan</string>
|
||||||
|
<string name="sus_su_mode">Mode SuS SU:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">konfirmasi pemasangan modul %1$s?</string>
|
<string name="module_install_confirm">konfirmasi pemasangan modul %1$s?</string>
|
||||||
<string name="unknown_module">module tidak dikenal</string>
|
<string name="unknown_module">module tidak dikenal</string>
|
||||||
|
|
@ -288,6 +290,8 @@
|
||||||
<string name="advanced_settings">Pengaturan Lanjutan</string>
|
<string name="advanced_settings">Pengaturan Lanjutan</string>
|
||||||
<string name="appearance_settings">Kustomisasi toolbar</string>
|
<string name="appearance_settings">Kustomisasi toolbar</string>
|
||||||
<string name="back">Kembali</string>
|
<string name="back">Kembali</string>
|
||||||
|
<string name="susfs_enabled">SuSFS dinyalakan</string>
|
||||||
|
<string name="susfs_disabled">SuSFS dimatikan</string>
|
||||||
<string name="background_set_success">Set latar belakang berhasil</string>
|
<string name="background_set_success">Set latar belakang berhasil</string>
|
||||||
<string name="background_removed">Latar belakang khusus yang dihapus</string>
|
<string name="background_removed">Latar belakang khusus yang dihapus</string>
|
||||||
<string name="icon_switch_title">Ubah ikon</string>
|
<string name="icon_switch_title">Ubah ikon</string>
|
||||||
|
|
@ -604,6 +608,7 @@
|
||||||
<string name="loop_paths_section">Jalur Loop</string>
|
<string name="loop_paths_section">Jalur Loop</string>
|
||||||
<string name="add_loop_path">Tambahkan Jalur Loop</string>
|
<string name="add_loop_path">Tambahkan Jalur Loop</string>
|
||||||
<!-- 循环路径功能描述 -->
|
<!-- 循环路径功能描述 -->
|
||||||
|
<string name="sus_loop_path_feature_label">Jalur Loop SUS</string>
|
||||||
<string name="sus_loop_paths_description_title">Konfigurasi Jalur Loop</string>
|
<string name="sus_loop_paths_description_title">Konfigurasi Jalur Loop</string>
|
||||||
<string name="sus_loop_paths_description_text">Jalur loop ditandai ulang sebagai SUS_PATH pada setiap startup aplikasi pengguna non-root atau layanan terisolasi. Ini membantu mengatasi masalah di mana jalur yang ditambahkan mungkin memiliki status inode direset atau inode dibuat ulang di kernel.</string>
|
<string name="sus_loop_paths_description_text">Jalur loop ditandai ulang sebagai SUS_PATH pada setiap startup aplikasi pengguna non-root atau layanan terisolasi. Ini membantu mengatasi masalah di mana jalur yang ditambahkan mungkin memiliki status inode direset atau inode dibuat ulang di kernel.</string>
|
||||||
<string name="avc_log_spoofing">Palsukan log AVC</string>
|
<string name="avc_log_spoofing">Palsukan log AVC</string>
|
||||||
|
|
@ -657,6 +662,7 @@ Diaktifkan: Aktifkan pemalsuan sus tcontext dari \'su\' dengan \'kernel\' yang d
|
||||||
<string name="uid_multi_user_scan_title">Pemindaian Aplikasi Multi-Pengguna</string>
|
<string name="uid_multi_user_scan_title">Pemindaian Aplikasi Multi-Pengguna</string>
|
||||||
<string name="uid_multi_user_scan_summary">Ketika diaktifkan, fitur ini akan memindai aplikasi untuk semua pengguna, termasuk profil kerja</string>
|
<string name="uid_multi_user_scan_summary">Ketika diaktifkan, fitur ini akan memindai aplikasi untuk semua pengguna, termasuk profil kerja</string>
|
||||||
<string name="uid_scanner_setting_failed">Gagal mengatur, silakan periksa perizinan</string>
|
<string name="uid_scanner_setting_failed">Gagal mengatur, silakan periksa perizinan</string>
|
||||||
|
<string name="uid_scanner_setting_error">Gagal mengatur: %s</string>
|
||||||
<string name="clean_runtime_environment">Bersihkan Lingkungan Runtime</string>
|
<string name="clean_runtime_environment">Bersihkan Lingkungan Runtime</string>
|
||||||
<string name="clean_runtime_environment_summary">Bersihkan berkas runtime dan hentikan layanan pemindai</string>
|
<string name="clean_runtime_environment_summary">Bersihkan berkas runtime dan hentikan layanan pemindai</string>
|
||||||
<string name="clean_runtime_environment_confirm">Apakah Anda yakin ingin membersihkan lingkungan runtime? Tindakan ini akan menghentikan layanan pemindai dan menghapus berkas yang terkait.</string>
|
<string name="clean_runtime_environment_confirm">Apakah Anda yakin ingin membersihkan lingkungan runtime? Tindakan ini akan menghentikan layanan pemindai dan menghapus berkas yang terkait.</string>
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@
|
||||||
<string name="selected_lkm">LKM selezionato: %s</string>
|
<string name="selected_lkm">LKM selezionato: %s</string>
|
||||||
<string name="save_log">Salva Registri</string>
|
<string name="save_log">Salva Registri</string>
|
||||||
<string name="log_saved">Logs saved</string>
|
<string name="log_saved">Logs saved</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -275,6 +276,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,7 @@
|
||||||
\n続行しますか?</string>
|
\n続行しますか?</string>
|
||||||
<string name="install_next">次へ</string>
|
<string name="install_next">次へ</string>
|
||||||
<string name="select_file_tip">%1$s のパーティションイメージを推奨します。</string>
|
<string name="select_file_tip">%1$s のパーティションイメージを推奨します。</string>
|
||||||
|
<string name="select_file_tip_vendor">(不安定)</string>
|
||||||
<string name="select_kmi">KMI を選択してください</string>
|
<string name="select_kmi">KMI を選択してください</string>
|
||||||
<string name="settings_uninstall">アンインストール</string>
|
<string name="settings_uninstall">アンインストール</string>
|
||||||
<string name="settings_uninstall_temporary">一時的にアンインストールする</string>
|
<string name="settings_uninstall_temporary">一時的にアンインストールする</string>
|
||||||
|
|
@ -126,6 +127,7 @@
|
||||||
<string name="selected_lkm">選択された LKM: %s</string>
|
<string name="selected_lkm">選択された LKM: %s</string>
|
||||||
<string name="save_log">ログを保存</string>
|
<string name="save_log">ログを保存</string>
|
||||||
<string name="log_saved">保存されたログ</string>
|
<string name="log_saved">保存されたログ</string>
|
||||||
|
<string name="sus_su_mode">SuS SU モード:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">%1$s モジュールをインストールしますか?</string>
|
<string name="module_install_confirm">%1$s モジュールをインストールしますか?</string>
|
||||||
<string name="unknown_module">不明なモジュール</string>
|
<string name="unknown_module">不明なモジュール</string>
|
||||||
|
|
@ -279,6 +281,8 @@
|
||||||
<string name="advanced_settings">高度な設定</string>
|
<string name="advanced_settings">高度な設定</string>
|
||||||
<string name="appearance_settings">ツールバーをカスタマイズ</string>
|
<string name="appearance_settings">ツールバーをカスタマイズ</string>
|
||||||
<string name="back">戻る</string>
|
<string name="back">戻る</string>
|
||||||
|
<string name="susfs_enabled">SuSFS 有効</string>
|
||||||
|
<string name="susfs_disabled">SuSFS 無効</string>
|
||||||
<string name="background_set_success">背景の設定が成功しました</string>
|
<string name="background_set_success">背景の設定が成功しました</string>
|
||||||
<string name="background_removed">カスタム背景を削除しました</string>
|
<string name="background_removed">カスタム背景を削除しました</string>
|
||||||
<string name="icon_switch_title">代替アイコン</string>
|
<string name="icon_switch_title">代替アイコン</string>
|
||||||
|
|
@ -593,6 +597,7 @@
|
||||||
<string name="loop_paths_section">ループパス</string>
|
<string name="loop_paths_section">ループパス</string>
|
||||||
<string name="add_loop_path">ループパスを追加</string>
|
<string name="add_loop_path">ループパスを追加</string>
|
||||||
<!-- 循环路径功能描述 -->
|
<!-- 循环路径功能描述 -->
|
||||||
|
<string name="sus_loop_path_feature_label">SUS ループパス</string>
|
||||||
<string name="sus_loop_paths_description_title">ループパスの構成</string>
|
<string name="sus_loop_paths_description_title">ループパスの構成</string>
|
||||||
<string name="sus_loop_paths_description_text">ループパスは、非 root ユーザーアプリまたは独立したサービスの起動ごとに SUS_PATH として再設定されます。これにより、追加されたパスの inode ステータスがリセットされたり、カーネル内で inode が再生成される問題に対処できます。</string>
|
<string name="sus_loop_paths_description_text">ループパスは、非 root ユーザーアプリまたは独立したサービスの起動ごとに SUS_PATH として再設定されます。これにより、追加されたパスの inode ステータスがリセットされたり、カーネル内で inode が再生成される問題に対処できます。</string>
|
||||||
<string name="avc_log_spoofing">AVC ログの偽装</string>
|
<string name="avc_log_spoofing">AVC ログの偽装</string>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">Selected LKM: %s</string>
|
<string name="selected_lkm">Selected LKM: %s</string>
|
||||||
<string name="save_log">ಲಾಗ್ಗಳನ್ನು ಉಳಿಸಿ</string>
|
<string name="save_log">ಲಾಗ್ಗಳನ್ನು ಉಳಿಸಿ</string>
|
||||||
<string name="log_saved">Logs saved</string>
|
<string name="log_saved">Logs saved</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -273,6 +274,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">선택된 LKM: %s</string>
|
<string name="selected_lkm">선택된 LKM: %s</string>
|
||||||
<string name="save_log">로그 저장</string>
|
<string name="save_log">로그 저장</string>
|
||||||
<string name="log_saved">로그 저장됨</string>
|
<string name="log_saved">로그 저장됨</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -273,6 +274,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">Selected LKM: %s</string>
|
<string name="selected_lkm">Selected LKM: %s</string>
|
||||||
<string name="save_log">Saglabāt Žurnālus</string>
|
<string name="save_log">Saglabāt Žurnālus</string>
|
||||||
<string name="log_saved">Logs saved</string>
|
<string name="log_saved">Logs saved</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -273,6 +274,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@
|
||||||
<string name="selected_lkm">Izvēlētais lkm: %s</string>
|
<string name="selected_lkm">Izvēlētais lkm: %s</string>
|
||||||
<string name="save_log">Išsaugoti Žurnalus</string>
|
<string name="save_log">Išsaugoti Žurnalus</string>
|
||||||
<string name="log_saved">Logs saved</string>
|
<string name="log_saved">Logs saved</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -275,6 +276,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">Selected LKM: %s</string>
|
<string name="selected_lkm">Selected LKM: %s</string>
|
||||||
<string name="save_log">लॉग जतन करा</string>
|
<string name="save_log">लॉग जतन करा</string>
|
||||||
<string name="log_saved">Logs saved</string>
|
<string name="log_saved">Logs saved</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -273,6 +274,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">Selected LKM: %s</string>
|
<string name="selected_lkm">Selected LKM: %s</string>
|
||||||
<string name="save_log">Simpan Log</string>
|
<string name="save_log">Simpan Log</string>
|
||||||
<string name="log_saved">Logs saved</string>
|
<string name="log_saved">Logs saved</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -273,6 +274,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@
|
||||||
<string name="selected_lkm">Geselecteerde LKM: %s</string>
|
<string name="selected_lkm">Geselecteerde LKM: %s</string>
|
||||||
<string name="save_log">Logboeken Opslaan</string>
|
<string name="save_log">Logboeken Opslaan</string>
|
||||||
<string name="log_saved">Logs opgeslagen</string>
|
<string name="log_saved">Logs opgeslagen</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -275,6 +276,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@
|
||||||
<string name="selected_lkm">Wybrano LKM: %s</string>
|
<string name="selected_lkm">Wybrano LKM: %s</string>
|
||||||
<string name="save_log">Zapisz dzienniki</string>
|
<string name="save_log">Zapisz dzienniki</string>
|
||||||
<string name="log_saved">Dzienniki zapisane</string>
|
<string name="log_saved">Dzienniki zapisane</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -275,6 +276,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">LKM selecionado: %s</string>
|
<string name="selected_lkm">LKM selecionado: %s</string>
|
||||||
<string name="save_log">Salvar Registros</string>
|
<string name="save_log">Salvar Registros</string>
|
||||||
<string name="log_saved">Registros salvos</string>
|
<string name="log_saved">Registros salvos</string>
|
||||||
|
<string name="sus_su_mode">Modo SU SuSU:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">¿confirmar la instalación del módulo %1$s?</string>
|
<string name="module_install_confirm">¿confirmar la instalación del módulo %1$s?</string>
|
||||||
<string name="unknown_module">módulo desconocido</string>
|
<string name="unknown_module">módulo desconocido</string>
|
||||||
|
|
@ -273,6 +274,8 @@
|
||||||
<string name="advanced_settings">Configurações Avançadas</string>
|
<string name="advanced_settings">Configurações Avançadas</string>
|
||||||
<string name="appearance_settings">Personaliza a barra de ferramentas.</string>
|
<string name="appearance_settings">Personaliza a barra de ferramentas.</string>
|
||||||
<string name="back">Retorno</string>
|
<string name="back">Retorno</string>
|
||||||
|
<string name="susfs_enabled">SuSFS habilitado</string>
|
||||||
|
<string name="susfs_disabled">SuSFS desativado</string>
|
||||||
<string name="background_set_success">Fundo definido com sucesso</string>
|
<string name="background_set_success">Fundo definido com sucesso</string>
|
||||||
<string name="background_removed">Remover</string>
|
<string name="background_removed">Remover</string>
|
||||||
<string name="icon_switch_title">Ícone alternativo</string>
|
<string name="icon_switch_title">Ícone alternativo</string>
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@
|
||||||
<string name="selected_lkm">Lkm selectat: %s</string>
|
<string name="selected_lkm">Lkm selectat: %s</string>
|
||||||
<string name="save_log">Salvează Jurnale</string>
|
<string name="save_log">Salvează Jurnale</string>
|
||||||
<string name="log_saved">Logs saved</string>
|
<string name="log_saved">Logs saved</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -275,6 +276,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@
|
||||||
<string name="install_upload_lkm_file">Использовать локальный файл LKM</string>
|
<string name="install_upload_lkm_file">Использовать локальный файл LKM</string>
|
||||||
<string name="install_only_support_ko_file">Поддерживаются только файлы .ko</string>
|
<string name="install_only_support_ko_file">Поддерживаются только файлы .ko</string>
|
||||||
<string name="select_file_tip">Образ раздела %1$s рекомендуется</string>
|
<string name="select_file_tip">Образ раздела %1$s рекомендуется</string>
|
||||||
|
<string name="select_file_tip_vendor">(нестабильный)</string>
|
||||||
<string name="select_kmi">Выбрать KMI</string>
|
<string name="select_kmi">Выбрать KMI</string>
|
||||||
<string name="settings_uninstall">Удалить</string>
|
<string name="settings_uninstall">Удалить</string>
|
||||||
<string name="settings_uninstall_temporary">Удалить на время</string>
|
<string name="settings_uninstall_temporary">Удалить на время</string>
|
||||||
|
|
@ -129,6 +130,7 @@
|
||||||
<string name="selected_lkm">Выбран LKM: %s</string>
|
<string name="selected_lkm">Выбран LKM: %s</string>
|
||||||
<string name="save_log">Сохранить логи</string>
|
<string name="save_log">Сохранить логи</string>
|
||||||
<string name="log_saved">Логи сохранены</string>
|
<string name="log_saved">Логи сохранены</string>
|
||||||
|
<string name="sus_su_mode">SuS SU режим:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">подтвердите установку модуля %1$s?</string>
|
<string name="module_install_confirm">подтвердите установку модуля %1$s?</string>
|
||||||
<string name="unknown_module">неизвестный модуль</string>
|
<string name="unknown_module">неизвестный модуль</string>
|
||||||
|
|
@ -289,6 +291,8 @@
|
||||||
<string name="advanced_settings">Расширенные</string>
|
<string name="advanced_settings">Расширенные</string>
|
||||||
<string name="appearance_settings">Внешний вид</string>
|
<string name="appearance_settings">Внешний вид</string>
|
||||||
<string name="back">Возвращение</string>
|
<string name="back">Возвращение</string>
|
||||||
|
<string name="susfs_enabled">SuSFS включен</string>
|
||||||
|
<string name="susfs_disabled">SuSFS выключен</string>
|
||||||
<string name="background_set_success">Фон успешно установлен</string>
|
<string name="background_set_success">Фон успешно установлен</string>
|
||||||
<string name="background_removed">Пользовательский фон удалён</string>
|
<string name="background_removed">Пользовательский фон удалён</string>
|
||||||
<string name="icon_switch_title">Альт. иконка</string>
|
<string name="icon_switch_title">Альт. иконка</string>
|
||||||
|
|
@ -605,6 +609,7 @@
|
||||||
<string name="loop_paths_section">Циклические пути</string>
|
<string name="loop_paths_section">Циклические пути</string>
|
||||||
<string name="add_loop_path">Добавить циклический путь</string>
|
<string name="add_loop_path">Добавить циклический путь</string>
|
||||||
<!-- 循环路径功能描述 -->
|
<!-- 循环路径功能描述 -->
|
||||||
|
<string name="sus_loop_path_feature_label">Циклический путь SUS</string>
|
||||||
<string name="sus_loop_paths_description_title">Конфигурация пути цикла</string>
|
<string name="sus_loop_paths_description_title">Конфигурация пути цикла</string>
|
||||||
<string name="sus_loop_paths_description_text">Пути цикла повторно отмечены как SUS_PATH в каждом пользовательском приложении, не являющемся root, или изолированном запуске службы. Это помогает решить проблемы, в которых добавленные пути могут иметь сброс статуса inode или повторно созданные inode в ядре.</string>
|
<string name="sus_loop_paths_description_text">Пути цикла повторно отмечены как SUS_PATH в каждом пользовательском приложении, не являющемся root, или изолированном запуске службы. Это помогает решить проблемы, в которых добавленные пути могут иметь сброс статуса inode или повторно созданные inode в ядре.</string>
|
||||||
<string name="avc_log_spoofing">Спуф AVC лога</string>
|
<string name="avc_log_spoofing">Спуф AVC лога</string>
|
||||||
|
|
@ -657,6 +662,7 @@
|
||||||
<string name="uid_multi_user_scan_title">Поиск многопользовательских приложений</string>
|
<string name="uid_multi_user_scan_title">Поиск многопользовательских приложений</string>
|
||||||
<string name="uid_multi_user_scan_summary">Когда включено, сканирует приложения для всех пользователей, включая рабочие профили</string>
|
<string name="uid_multi_user_scan_summary">Когда включено, сканирует приложения для всех пользователей, включая рабочие профили</string>
|
||||||
<string name="uid_scanner_setting_failed">Не удалось установить, проверьте права доступа</string>
|
<string name="uid_scanner_setting_failed">Не удалось установить, проверьте права доступа</string>
|
||||||
|
<string name="uid_scanner_setting_error">Не удалось установить: %s</string>
|
||||||
<string name="clean_runtime_environment">Очистить среду Runtime</string>
|
<string name="clean_runtime_environment">Очистить среду Runtime</string>
|
||||||
<string name="clean_runtime_environment_summary">Очистить среду Runtime и остановить службу сканирования</string>
|
<string name="clean_runtime_environment_summary">Очистить среду Runtime и остановить службу сканирования</string>
|
||||||
<string name="clean_runtime_environment_confirm">Вы уверены, что хотите очистить среду Runtime? Это остановит службу сканирования и удалит связанные с ней файлы.</string>
|
<string name="clean_runtime_environment_confirm">Вы уверены, что хотите очистить среду Runtime? Это остановит службу сканирования и удалит связанные с ней файлы.</string>
|
||||||
|
|
@ -699,6 +705,9 @@
|
||||||
<string name="log_viewer_clear_logs">Очистить логи</string>
|
<string name="log_viewer_clear_logs">Очистить логи</string>
|
||||||
<string name="log_viewer_clear_logs_confirm">Вы уверены, что хотите удалить выбранный файл журнала? Это действие нельзя отменить.</string>
|
<string name="log_viewer_clear_logs_confirm">Вы уверены, что хотите удалить выбранный файл журнала? Это действие нельзя отменить.</string>
|
||||||
<string name="log_viewer_logs_cleared">Логи успешно очищены</string>
|
<string name="log_viewer_logs_cleared">Логи успешно очищены</string>
|
||||||
|
<string name="log_viewer_select_file">Выберите файл журнала</string>
|
||||||
|
<string name="log_viewer_current_log">Текущий лог</string>
|
||||||
|
<string name="log_viewer_old_log">Старый лог</string>
|
||||||
<string name="log_viewer_filter_type">Фильтрация по типу</string>
|
<string name="log_viewer_filter_type">Фильтрация по типу</string>
|
||||||
<string name="log_viewer_all_types">Все типы</string>
|
<string name="log_viewer_all_types">Все типы</string>
|
||||||
<string name="log_viewer_showing_entries">Показаны записи %1$d из %2$d</string>
|
<string name="log_viewer_showing_entries">Показаны записи %1$d из %2$d</string>
|
||||||
|
|
@ -726,6 +735,8 @@
|
||||||
<string name="umount_path_restart_notice">Для применения изменений необходима перезагрузка. Система применит новый конфиг при следующем запуске.</string>
|
<string name="umount_path_restart_notice">Для применения изменений необходима перезагрузка. Система применит новый конфиг при следующем запуске.</string>
|
||||||
<string name="add_umount_path">Добавить путь размонтирования</string>
|
<string name="add_umount_path">Добавить путь размонтирования</string>
|
||||||
<string name="mount_path">Смонтировать путь</string>
|
<string name="mount_path">Смонтировать путь</string>
|
||||||
|
<string name="check_mount_type">Проверить тип монтирования</string>
|
||||||
|
<string name="check_mount_type_overlay">Проверить, является ли оверлеем</string>
|
||||||
<string name="umount_flags">Флаги размонтирования</string>
|
<string name="umount_flags">Флаги размонтирования</string>
|
||||||
<string name="umount_flags_hint">0=Обычное размонтирование, 8=MNT_DETACH, -1=Автоматически</string>
|
<string name="umount_flags_hint">0=Обычное размонтирование, 8=MNT_DETACH, -1=Автоматически</string>
|
||||||
<string name="flags">Флаги</string>
|
<string name="flags">Флаги</string>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">Selected LKM: %s</string>
|
<string name="selected_lkm">Selected LKM: %s</string>
|
||||||
<string name="save_log">Shrani Dnevnike</string>
|
<string name="save_log">Shrani Dnevnike</string>
|
||||||
<string name="log_saved">Logs saved</string>
|
<string name="log_saved">Logs saved</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -273,6 +274,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@
|
||||||
<string name="selected_lkm">เลือก LKM: %s</string>
|
<string name="selected_lkm">เลือก LKM: %s</string>
|
||||||
<string name="save_log">บันทึกบันทึก</string>
|
<string name="save_log">บันทึกบันทึก</string>
|
||||||
<string name="log_saved">บันทึก Log แล้ว</string>
|
<string name="log_saved">บันทึก Log แล้ว</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">unknown module</string>
|
<string name="unknown_module">unknown module</string>
|
||||||
|
|
@ -275,6 +276,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,7 @@
|
||||||
<string name="install_upload_lkm_file">Yerel LKM dosyası kullan</string>
|
<string name="install_upload_lkm_file">Yerel LKM dosyası kullan</string>
|
||||||
<string name="install_only_support_ko_file">Yalnızca .ko dosyaları desteklenir</string>
|
<string name="install_only_support_ko_file">Yalnızca .ko dosyaları desteklenir</string>
|
||||||
<string name="select_file_tip">%1$s bölüm görüntüsü önerilir</string>
|
<string name="select_file_tip">%1$s bölüm görüntüsü önerilir</string>
|
||||||
|
<string name="select_file_tip_vendor">(kararsız)</string>
|
||||||
<string name="select_kmi">KMI seçin</string>
|
<string name="select_kmi">KMI seçin</string>
|
||||||
<string name="settings_uninstall">Kaldır</string>
|
<string name="settings_uninstall">Kaldır</string>
|
||||||
<string name="settings_uninstall_temporary">Geçici olarak kaldır</string>
|
<string name="settings_uninstall_temporary">Geçici olarak kaldır</string>
|
||||||
|
|
@ -126,6 +127,7 @@
|
||||||
<string name="selected_lkm">Seçilen LKM: %s</string>
|
<string name="selected_lkm">Seçilen LKM: %s</string>
|
||||||
<string name="save_log">Günlükleri kaydet</string>
|
<string name="save_log">Günlükleri kaydet</string>
|
||||||
<string name="log_saved">Günlükler kaydedildi</string>
|
<string name="log_saved">Günlükler kaydedildi</string>
|
||||||
|
<string name="sus_su_mode">SuS SU modu:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">%1$s modülünü yüklemek istediğinizden emin misiniz?</string>
|
<string name="module_install_confirm">%1$s modülünü yüklemek istediğinizden emin misiniz?</string>
|
||||||
<string name="unknown_module">Bilinmeyen modül</string>
|
<string name="unknown_module">Bilinmeyen modül</string>
|
||||||
|
|
@ -286,6 +288,8 @@
|
||||||
<string name="advanced_settings">Gelişmiş Ayarlar</string>
|
<string name="advanced_settings">Gelişmiş Ayarlar</string>
|
||||||
<string name="appearance_settings">Araç çubuğunu özelleştir</string>
|
<string name="appearance_settings">Araç çubuğunu özelleştir</string>
|
||||||
<string name="back">Geri</string>
|
<string name="back">Geri</string>
|
||||||
|
<string name="susfs_enabled">SuSFS etkin</string>
|
||||||
|
<string name="susfs_disabled">SuSFS devre dışı</string>
|
||||||
<string name="background_set_success">Arka plan başarıyla ayarlandı</string>
|
<string name="background_set_success">Arka plan başarıyla ayarlandı</string>
|
||||||
<string name="background_removed">Özel arka planlar kaldırıldı</string>
|
<string name="background_removed">Özel arka planlar kaldırıldı</string>
|
||||||
<string name="icon_switch_title">Alternatif simge</string>
|
<string name="icon_switch_title">Alternatif simge</string>
|
||||||
|
|
@ -602,6 +606,7 @@
|
||||||
<string name="loop_paths_section">Döngü Yolları</string>
|
<string name="loop_paths_section">Döngü Yolları</string>
|
||||||
<string name="add_loop_path">Döngü Yolu Ekle</string>
|
<string name="add_loop_path">Döngü Yolu Ekle</string>
|
||||||
<!-- 循环路径功能描述 -->
|
<!-- 循环路径功能描述 -->
|
||||||
|
<string name="sus_loop_path_feature_label">SUS Döngü Yolu</string>
|
||||||
<string name="sus_loop_paths_description_title">Döngü Yolu Yapılandırması</string>
|
<string name="sus_loop_paths_description_title">Döngü Yolu Yapılandırması</string>
|
||||||
<string name="sus_loop_paths_description_text">Döngü yolları, her kök olmayan (non-root) kullanıcı uygulaması veya yalıtılmış hizmet başlangıcında SUS_PATH olarak yeniden işaretlenir. Bu, eklenen yolların inode durumunun sıfırlanması veya çekirdekte yeniden oluşturulması gibi sorunları gidermeye yardımcı olur.</string>
|
<string name="sus_loop_paths_description_text">Döngü yolları, her kök olmayan (non-root) kullanıcı uygulaması veya yalıtılmış hizmet başlangıcında SUS_PATH olarak yeniden işaretlenir. Bu, eklenen yolların inode durumunun sıfırlanması veya çekirdekte yeniden oluşturulması gibi sorunları gidermeye yardımcı olur.</string>
|
||||||
<string name="avc_log_spoofing">AVC Günlük Kaydı Taklidi</string>
|
<string name="avc_log_spoofing">AVC Günlük Kaydı Taklidi</string>
|
||||||
|
|
@ -655,6 +660,7 @@ etkin: Çekirdekteki AVC günlük kaydında, \'su\' komutuna ait tcontext\'i \'k
|
||||||
<string name="uid_multi_user_scan_title">Çok Kullanıcılı Uygulama Taraması</string>
|
<string name="uid_multi_user_scan_title">Çok Kullanıcılı Uygulama Taraması</string>
|
||||||
<string name="uid_multi_user_scan_summary">Etkinleştirildiğinde, iş profilleri de dahil olmak üzere tüm kullanıcıların uygulamalarını tarar.</string>
|
<string name="uid_multi_user_scan_summary">Etkinleştirildiğinde, iş profilleri de dahil olmak üzere tüm kullanıcıların uygulamalarını tarar.</string>
|
||||||
<string name="uid_scanner_setting_failed">Ayar başarısız oldu, lütfen izinleri kontrol edin</string>
|
<string name="uid_scanner_setting_failed">Ayar başarısız oldu, lütfen izinleri kontrol edin</string>
|
||||||
|
<string name="uid_scanner_setting_error">Ayar başarısız oldu: %s</string>
|
||||||
<string name="clean_runtime_environment">Çalışma Zamanı Ortamını Temizle</string>
|
<string name="clean_runtime_environment">Çalışma Zamanı Ortamını Temizle</string>
|
||||||
<string name="clean_runtime_environment_summary">Çalışma zamanı dosyalarını temizleyin ve tarayıcı hizmetini durdurun</string>
|
<string name="clean_runtime_environment_summary">Çalışma zamanı dosyalarını temizleyin ve tarayıcı hizmetini durdurun</string>
|
||||||
<string name="clean_runtime_environment_confirm">Çalışma zamanı ortamını temizlemek istediğinizden emin misiniz? Bu işlem tarayıcı hizmetini durduracak ve ilgili dosyaları kaldıracaktır.</string>
|
<string name="clean_runtime_environment_confirm">Çalışma zamanı ortamını temizlemek istediğinizden emin misiniz? Bu işlem tarayıcı hizmetini durduracak ve ilgili dosyaları kaldıracaktır.</string>
|
||||||
|
|
@ -697,6 +703,9 @@ etkin: Çekirdekteki AVC günlük kaydında, \'su\' komutuna ait tcontext\'i \'k
|
||||||
<string name="log_viewer_clear_logs">Günlükleri Temizle</string>
|
<string name="log_viewer_clear_logs">Günlükleri Temizle</string>
|
||||||
<string name="log_viewer_clear_logs_confirm">Seçili günlük dosyasını temizlemek istediğinizden emin misiniz? Bu işlem geri alınamaz.</string>
|
<string name="log_viewer_clear_logs_confirm">Seçili günlük dosyasını temizlemek istediğinizden emin misiniz? Bu işlem geri alınamaz.</string>
|
||||||
<string name="log_viewer_logs_cleared">Günlükler başarıyla temizlendi</string>
|
<string name="log_viewer_logs_cleared">Günlükler başarıyla temizlendi</string>
|
||||||
|
<string name="log_viewer_select_file">Günlük Dosyası Seç</string>
|
||||||
|
<string name="log_viewer_current_log">Mevcut Günlük</string>
|
||||||
|
<string name="log_viewer_old_log">Eski Günlük</string>
|
||||||
<string name="log_viewer_filter_type">Türe Göre Filtrele</string>
|
<string name="log_viewer_filter_type">Türe Göre Filtrele</string>
|
||||||
<string name="log_viewer_all_types">Tüm Türler</string>
|
<string name="log_viewer_all_types">Tüm Türler</string>
|
||||||
<string name="log_viewer_showing_entries">%2$d girişten %1$d tanesi gösteriliyor</string>
|
<string name="log_viewer_showing_entries">%2$d girişten %1$d tanesi gösteriliyor</string>
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@
|
||||||
<string name="install_inactive_slot_warning">Ваш пристрій буде **ПРИМУСОВО** завантажено в поточний неактивний слот після перезавантаження!\nВикористовуйте цю опцію лише після завершення OTA.\nПродовжити?</string>
|
<string name="install_inactive_slot_warning">Ваш пристрій буде **ПРИМУСОВО** завантажено в поточний неактивний слот після перезавантаження!\nВикористовуйте цю опцію лише після завершення OTA.\nПродовжити?</string>
|
||||||
<string name="install_next">Далі</string>
|
<string name="install_next">Далі</string>
|
||||||
<string name="select_file_tip">Рекомендується образ розділу %1$s</string>
|
<string name="select_file_tip">Рекомендується образ розділу %1$s</string>
|
||||||
|
<string name="select_file_tip_vendor">(нестабільно)</string>
|
||||||
<string name="select_kmi">Вибрати KMI</string>
|
<string name="select_kmi">Вибрати KMI</string>
|
||||||
<string name="settings_uninstall">Видалити</string>
|
<string name="settings_uninstall">Видалити</string>
|
||||||
<string name="settings_uninstall_temporary">Тимчасово видалити</string>
|
<string name="settings_uninstall_temporary">Тимчасово видалити</string>
|
||||||
|
|
@ -125,6 +126,7 @@
|
||||||
<string name="selected_lkm">Обраний LKM: %s</string>
|
<string name="selected_lkm">Обраний LKM: %s</string>
|
||||||
<string name="save_log">Зберегти логи</string>
|
<string name="save_log">Зберегти логи</string>
|
||||||
<string name="log_saved">Логи збережено</string>
|
<string name="log_saved">Логи збережено</string>
|
||||||
|
<string name="sus_su_mode">Режим SuS SU:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">Підтвердити встановлення модуля %1$s?</string>
|
<string name="module_install_confirm">Підтвердити встановлення модуля %1$s?</string>
|
||||||
<string name="unknown_module">невідомий модуль</string>
|
<string name="unknown_module">невідомий модуль</string>
|
||||||
|
|
@ -276,6 +278,8 @@
|
||||||
<string name="advanced_settings">Розширені налаштування</string>
|
<string name="advanced_settings">Розширені налаштування</string>
|
||||||
<string name="appearance_settings">Налаштувати панель інструментів</string>
|
<string name="appearance_settings">Налаштувати панель інструментів</string>
|
||||||
<string name="back">Повернутися</string>
|
<string name="back">Повернутися</string>
|
||||||
|
<string name="susfs_enabled">SuSFS увімкнено</string>
|
||||||
|
<string name="susfs_disabled">SuSFS вимкнено</string>
|
||||||
<string name="background_set_success">Фон успішно встановлено</string>
|
<string name="background_set_success">Фон успішно встановлено</string>
|
||||||
<string name="background_removed">Видалено власні фони</string>
|
<string name="background_removed">Видалено власні фони</string>
|
||||||
<string name="icon_switch_title">Альтернативна іконка</string>
|
<string name="icon_switch_title">Альтернативна іконка</string>
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,7 @@
|
||||||
<string name="install_inactive_slot_warning">Thiết bị của bạn sẽ **BUỘC** phải khởi động vào phân vùng chưa được sử dụng!\nChỉ sử dụng tùy chọn này sau khi cập nhật OTA hoàn tất.\nTiếp tục?</string>
|
<string name="install_inactive_slot_warning">Thiết bị của bạn sẽ **BUỘC** phải khởi động vào phân vùng chưa được sử dụng!\nChỉ sử dụng tùy chọn này sau khi cập nhật OTA hoàn tất.\nTiếp tục?</string>
|
||||||
<string name="install_next">Kế tiếp</string>
|
<string name="install_next">Kế tiếp</string>
|
||||||
<string name="select_file_tip">Phân vùng %1$s được khuyến nghị</string>
|
<string name="select_file_tip">Phân vùng %1$s được khuyến nghị</string>
|
||||||
|
<string name="select_file_tip_vendor">(Thử nghiệm)</string>
|
||||||
<string name="select_kmi">Chọn KMI</string>
|
<string name="select_kmi">Chọn KMI</string>
|
||||||
<string name="settings_uninstall">Gỡ cài đặt</string>
|
<string name="settings_uninstall">Gỡ cài đặt</string>
|
||||||
<string name="settings_uninstall_temporary">Gỡ cài đặt tạm thời</string>
|
<string name="settings_uninstall_temporary">Gỡ cài đặt tạm thời</string>
|
||||||
|
|
@ -124,6 +125,7 @@
|
||||||
<string name="selected_lkm">Đã chọn LKM: %s</string>
|
<string name="selected_lkm">Đã chọn LKM: %s</string>
|
||||||
<string name="save_log">Lưu nhật ký</string>
|
<string name="save_log">Lưu nhật ký</string>
|
||||||
<string name="log_saved">Nhật ký đã được lưu</string>
|
<string name="log_saved">Nhật ký đã được lưu</string>
|
||||||
|
<string name="sus_su_mode">Chế độ SuS SU:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">Xác nhận cài đặt module %1$s?</string>
|
<string name="module_install_confirm">Xác nhận cài đặt module %1$s?</string>
|
||||||
<string name="unknown_module">Module không xác định</string>
|
<string name="unknown_module">Module không xác định</string>
|
||||||
|
|
@ -277,6 +279,8 @@
|
||||||
<string name="advanced_settings">Cài đặt nâng cao</string>
|
<string name="advanced_settings">Cài đặt nâng cao</string>
|
||||||
<string name="appearance_settings">Cài đặt giao diện</string>
|
<string name="appearance_settings">Cài đặt giao diện</string>
|
||||||
<string name="back">Trở lại</string>
|
<string name="back">Trở lại</string>
|
||||||
|
<string name="susfs_enabled">SuSFS đã bật</string>
|
||||||
|
<string name="susfs_disabled">SuSFS đã tắt</string>
|
||||||
<string name="background_set_success">Đã cài đặt hình nền thành công</string>
|
<string name="background_set_success">Đã cài đặt hình nền thành công</string>
|
||||||
<string name="background_removed">Đã xóa hình nền tùy chỉnh</string>
|
<string name="background_removed">Đã xóa hình nền tùy chỉnh</string>
|
||||||
<string name="icon_switch_title">Thay thế icon</string>
|
<string name="icon_switch_title">Thay thế icon</string>
|
||||||
|
|
@ -593,6 +597,7 @@
|
||||||
<string name="loop_paths_section">Đường dẫn Vòng lặp</string>
|
<string name="loop_paths_section">Đường dẫn Vòng lặp</string>
|
||||||
<string name="add_loop_path">Thêm Đường dẫn Vòng lặp</string>
|
<string name="add_loop_path">Thêm Đường dẫn Vòng lặp</string>
|
||||||
<!-- 循环路径功能描述 -->
|
<!-- 循环路径功能描述 -->
|
||||||
|
<string name="sus_loop_path_feature_label">Đường dẫn Vòng lặp SuS</string>
|
||||||
<string name="sus_loop_paths_description_title">Cấu hình Đường dẫn Vòng lặp</string>
|
<string name="sus_loop_paths_description_title">Cấu hình Đường dẫn Vòng lặp</string>
|
||||||
<string name="sus_loop_paths_description_text">Đường dẫn Vòng lặp được đổi tên thành SUS_PATH mỗi khi một ứng dụng không phải root hoặc dịch vụ cô lập được khởi động. Điều này giúp giải quyết vấn đề đường dẫn đã thêm có thể trở nên không hợp lệ do trạng thái inode được đặt lại hoặc inode được tạo lại trong Kernel</string>
|
<string name="sus_loop_paths_description_text">Đường dẫn Vòng lặp được đổi tên thành SUS_PATH mỗi khi một ứng dụng không phải root hoặc dịch vụ cô lập được khởi động. Điều này giúp giải quyết vấn đề đường dẫn đã thêm có thể trở nên không hợp lệ do trạng thái inode được đặt lại hoặc inode được tạo lại trong Kernel</string>
|
||||||
<string name="avc_log_spoofing">Giả mạo nhật ký AVC</string>
|
<string name="avc_log_spoofing">Giả mạo nhật ký AVC</string>
|
||||||
|
|
@ -646,6 +651,7 @@ Bật: Kích hoạt tính năng giả mạo sus tcontext của \'su\' thành \'k
|
||||||
<string name="uid_multi_user_scan_title">Quét ứng dụng nhiều người dùng</string>
|
<string name="uid_multi_user_scan_title">Quét ứng dụng nhiều người dùng</string>
|
||||||
<string name="uid_multi_user_scan_summary">Khi được bật, tất cả ứng dụng của người dùng sẽ được quét, bao gồm cả dữ liệu công việc, v.v</string>
|
<string name="uid_multi_user_scan_summary">Khi được bật, tất cả ứng dụng của người dùng sẽ được quét, bao gồm cả dữ liệu công việc, v.v</string>
|
||||||
<string name="uid_scanner_setting_failed">Thiết lập thất bại, vui lòng kiểm tra quyền</string>
|
<string name="uid_scanner_setting_failed">Thiết lập thất bại, vui lòng kiểm tra quyền</string>
|
||||||
|
<string name="uid_scanner_setting_error">Thiết lập thất bại: %s</string>
|
||||||
<string name="clean_runtime_environment">Dọn dẹp môi trường hoạt động</string>
|
<string name="clean_runtime_environment">Dọn dẹp môi trường hoạt động</string>
|
||||||
<string name="clean_runtime_environment_summary">Dọn dẹp các file hoạt động và dừng quét các dịch vụ</string>
|
<string name="clean_runtime_environment_summary">Dọn dẹp các file hoạt động và dừng quét các dịch vụ</string>
|
||||||
<string name="clean_runtime_environment_confirm">Bạn có chắc chắn muốn dọn dẹp môi trường hoạt động không? Thao tác này sẽ dừng dịch vụ quét và xóa các file liên quan</string>
|
<string name="clean_runtime_environment_confirm">Bạn có chắc chắn muốn dọn dẹp môi trường hoạt động không? Thao tác này sẽ dừng dịch vụ quét và xóa các file liên quan</string>
|
||||||
|
|
@ -688,6 +694,9 @@ Bật: Kích hoạt tính năng giả mạo sus tcontext của \'su\' thành \'k
|
||||||
<string name="log_viewer_clear_logs">Xoá nhật ký</string>
|
<string name="log_viewer_clear_logs">Xoá nhật ký</string>
|
||||||
<string name="log_viewer_clear_logs_confirm">Bạn có chắc chắn muốn xóa tệp nhật ký đã chọn không? Thao tác này không thể hoàn tác</string>
|
<string name="log_viewer_clear_logs_confirm">Bạn có chắc chắn muốn xóa tệp nhật ký đã chọn không? Thao tác này không thể hoàn tác</string>
|
||||||
<string name="log_viewer_logs_cleared">Đã xoá nhật ký thành công</string>
|
<string name="log_viewer_logs_cleared">Đã xoá nhật ký thành công</string>
|
||||||
|
<string name="log_viewer_select_file">Chọn tệp nhật ký</string>
|
||||||
|
<string name="log_viewer_current_log">Nhật ký hiện tại</string>
|
||||||
|
<string name="log_viewer_old_log">Nhật ký cũ</string>
|
||||||
<string name="log_viewer_filter_type">Lọc theo loại</string>
|
<string name="log_viewer_filter_type">Lọc theo loại</string>
|
||||||
<string name="log_viewer_all_types">Tất cả các loại</string>
|
<string name="log_viewer_all_types">Tất cả các loại</string>
|
||||||
<string name="log_viewer_showing_entries">Hiển thị %1$d trong tổng %2$d nhật ký</string>
|
<string name="log_viewer_showing_entries">Hiển thị %1$d trong tổng %2$d nhật ký</string>
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,7 @@
|
||||||
<string name="install_upload_lkm_file">使用本地 LKM 文件</string>
|
<string name="install_upload_lkm_file">使用本地 LKM 文件</string>
|
||||||
<string name="install_only_support_ko_file">仅支持选择 .ko 文件</string>
|
<string name="install_only_support_ko_file">仅支持选择 .ko 文件</string>
|
||||||
<string name="select_file_tip">建议选择 %1$s 分区镜像</string>
|
<string name="select_file_tip">建议选择 %1$s 分区镜像</string>
|
||||||
|
<string name="select_file_tip_vendor">(实验性的)</string>
|
||||||
<string name="select_kmi">选择 KMI</string>
|
<string name="select_kmi">选择 KMI</string>
|
||||||
<string name="settings_uninstall">卸载</string>
|
<string name="settings_uninstall">卸载</string>
|
||||||
<string name="settings_uninstall_temporary">临时卸载</string>
|
<string name="settings_uninstall_temporary">临时卸载</string>
|
||||||
|
|
@ -127,6 +128,7 @@
|
||||||
<string name="selected_lkm">已选择的 LKM:%s</string>
|
<string name="selected_lkm">已选择的 LKM:%s</string>
|
||||||
<string name="save_log">保存日志</string>
|
<string name="save_log">保存日志</string>
|
||||||
<string name="log_saved">日志已保存</string>
|
<string name="log_saved">日志已保存</string>
|
||||||
|
<string name="sus_su_mode">SuS SU 模式:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">确认安装模块 %1$s?</string>
|
<string name="module_install_confirm">确认安装模块 %1$s?</string>
|
||||||
<string name="unknown_module">未知模块</string>
|
<string name="unknown_module">未知模块</string>
|
||||||
|
|
@ -168,8 +170,6 @@
|
||||||
<string name="settings_disable_kernel_umount_summary">关闭 KernelSU 控制的内核级 umount 行为。</string>
|
<string name="settings_disable_kernel_umount_summary">关闭 KernelSU 控制的内核级 umount 行为。</string>
|
||||||
<string name="settings_enable_enhanced_security">增强安全性</string>
|
<string name="settings_enable_enhanced_security">增强安全性</string>
|
||||||
<string name="settings_enable_enhanced_security_summary">使用更严格的安全策略。</string>
|
<string name="settings_enable_enhanced_security_summary">使用更严格的安全策略。</string>
|
||||||
<string name="feature_status_unsupported_summary">内核不支持此功能。</string>
|
|
||||||
<string name="feature_status_managed_summary">此功能由模块管理。</string>
|
|
||||||
<string name="settings_mode_default">默认</string>
|
<string name="settings_mode_default">默认</string>
|
||||||
<string name="settings_mode_temp_enable">临时启用</string>
|
<string name="settings_mode_temp_enable">临时启用</string>
|
||||||
<string name="settings_mode_always_enable">始终启用</string>
|
<string name="settings_mode_always_enable">始终启用</string>
|
||||||
|
|
@ -289,6 +289,8 @@
|
||||||
<string name="advanced_settings">高级设置</string>
|
<string name="advanced_settings">高级设置</string>
|
||||||
<string name="appearance_settings">外观设置</string>
|
<string name="appearance_settings">外观设置</string>
|
||||||
<string name="back">返回</string>
|
<string name="back">返回</string>
|
||||||
|
<string name="susfs_enabled">SuSFS 已启用</string>
|
||||||
|
<string name="susfs_disabled">SuSFS 已禁用</string>
|
||||||
<string name="background_set_success">背景设置成功</string>
|
<string name="background_set_success">背景设置成功</string>
|
||||||
<string name="background_removed">已移除自定义背景</string>
|
<string name="background_removed">已移除自定义背景</string>
|
||||||
<string name="icon_switch_title">备选图标</string>
|
<string name="icon_switch_title">备选图标</string>
|
||||||
|
|
@ -605,6 +607,7 @@
|
||||||
<string name="loop_paths_section">循环路径</string>
|
<string name="loop_paths_section">循环路径</string>
|
||||||
<string name="add_loop_path">添加循环路径</string>
|
<string name="add_loop_path">添加循环路径</string>
|
||||||
<!-- 循环路径功能描述 -->
|
<!-- 循环路径功能描述 -->
|
||||||
|
<string name="sus_loop_path_feature_label">SuS 循环路径</string>
|
||||||
<string name="sus_loop_paths_description_title">循环路径配置</string>
|
<string name="sus_loop_paths_description_title">循环路径配置</string>
|
||||||
<string name="sus_loop_paths_description_text">循环路径会在每次非 root 用户应用或隔离服务启动时重新标记为 SUS_PATH。这有助于解决添加的路径可能因 inode 状态重置或内核中 inode 重新创建而失效的问题</string>
|
<string name="sus_loop_paths_description_text">循环路径会在每次非 root 用户应用或隔离服务启动时重新标记为 SUS_PATH。这有助于解决添加的路径可能因 inode 状态重置或内核中 inode 重新创建而失效的问题</string>
|
||||||
<string name="avc_log_spoofing">AVC 日志欺骗</string>
|
<string name="avc_log_spoofing">AVC 日志欺骗</string>
|
||||||
|
|
@ -658,6 +661,7 @@
|
||||||
<string name="uid_multi_user_scan_title">多用户应用扫描</string>
|
<string name="uid_multi_user_scan_title">多用户应用扫描</string>
|
||||||
<string name="uid_multi_user_scan_summary">开启后将扫描所有用户的应用,包括工作资料等</string>
|
<string name="uid_multi_user_scan_summary">开启后将扫描所有用户的应用,包括工作资料等</string>
|
||||||
<string name="uid_scanner_setting_failed">设置失败,请检查权限</string>
|
<string name="uid_scanner_setting_failed">设置失败,请检查权限</string>
|
||||||
|
<string name="uid_scanner_setting_error">设置失败: %s</string>
|
||||||
<string name="clean_runtime_environment">清理运行环境</string>
|
<string name="clean_runtime_environment">清理运行环境</string>
|
||||||
<string name="clean_runtime_environment_summary">清理运行时文件并停止扫描服务</string>
|
<string name="clean_runtime_environment_summary">清理运行时文件并停止扫描服务</string>
|
||||||
<string name="clean_runtime_environment_confirm">您确定要清理运行环境吗?这将停止扫描服务并删除相关文件</string>
|
<string name="clean_runtime_environment_confirm">您确定要清理运行环境吗?这将停止扫描服务并删除相关文件</string>
|
||||||
|
|
@ -700,6 +704,9 @@
|
||||||
<string name="log_viewer_clear_logs">清空日志</string>
|
<string name="log_viewer_clear_logs">清空日志</string>
|
||||||
<string name="log_viewer_clear_logs_confirm">确定要清空选中的日志文件吗?此操作无法撤销。</string>
|
<string name="log_viewer_clear_logs_confirm">确定要清空选中的日志文件吗?此操作无法撤销。</string>
|
||||||
<string name="log_viewer_logs_cleared">日志清空成功</string>
|
<string name="log_viewer_logs_cleared">日志清空成功</string>
|
||||||
|
<string name="log_viewer_select_file">选择日志文件</string>
|
||||||
|
<string name="log_viewer_current_log">当前日志</string>
|
||||||
|
<string name="log_viewer_old_log">旧日志</string>
|
||||||
<string name="log_viewer_filter_type">按类型筛选</string>
|
<string name="log_viewer_filter_type">按类型筛选</string>
|
||||||
<string name="log_viewer_all_types">所有类型</string>
|
<string name="log_viewer_all_types">所有类型</string>
|
||||||
<string name="log_viewer_showing_entries">显示 %1$d / %2$d 条记录</string>
|
<string name="log_viewer_showing_entries">显示 %1$d / %2$d 条记录</string>
|
||||||
|
|
@ -726,6 +733,8 @@
|
||||||
<string name="umount_path_restart_notice">添加或删除路径后需要重启设备才能生效。系统会在下次启动时应用新的配置。</string>
|
<string name="umount_path_restart_notice">添加或删除路径后需要重启设备才能生效。系统会在下次启动时应用新的配置。</string>
|
||||||
<string name="add_umount_path">添加 Umount 路径</string>
|
<string name="add_umount_path">添加 Umount 路径</string>
|
||||||
<string name="mount_path">挂载路径</string>
|
<string name="mount_path">挂载路径</string>
|
||||||
|
<string name="check_mount_type">检查挂载类型</string>
|
||||||
|
<string name="check_mount_type_overlay">检查是否为 overlay 类型</string>
|
||||||
<string name="umount_flags">卸载标志</string>
|
<string name="umount_flags">卸载标志</string>
|
||||||
<string name="umount_flags_hint">0=正常卸载, 8=MNT_DETACH, -1=自动</string>
|
<string name="umount_flags_hint">0=正常卸载, 8=MNT_DETACH, -1=自动</string>
|
||||||
<string name="flags">标志</string>
|
<string name="flags">标志</string>
|
||||||
|
|
@ -742,6 +751,4 @@
|
||||||
<string name="apply_config">应用配置</string>
|
<string name="apply_config">应用配置</string>
|
||||||
<string name="config_applied">配置已应用到内核</string>
|
<string name="config_applied">配置已应用到内核</string>
|
||||||
<string name="group_contains_apps">包含 %1$d 个应用</string>
|
<string name="group_contains_apps">包含 %1$d 个应用</string>
|
||||||
<string name="settings_disable_sulog"> 禁用超级用户日志 </string>
|
|
||||||
<string name="settings_disable_sulog_summary">禁用 KernelSU 超级用户访问记录</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@
|
||||||
<string name="selected_lkm">選擇嘅 LKM:%s</string>
|
<string name="selected_lkm">選擇嘅 LKM:%s</string>
|
||||||
<string name="save_log">存儲日誌</string>
|
<string name="save_log">存儲日誌</string>
|
||||||
<string name="log_saved">日誌已存儲</string>
|
<string name="log_saved">日誌已存儲</string>
|
||||||
|
<string name="sus_su_mode">SuS SU 模式:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">確認安裝模組 %1$s?</string>
|
<string name="module_install_confirm">確認安裝模組 %1$s?</string>
|
||||||
<string name="unknown_module">未知模組</string>
|
<string name="unknown_module">未知模組</string>
|
||||||
|
|
@ -275,6 +276,8 @@
|
||||||
<string name="advanced_settings">高級配置</string>
|
<string name="advanced_settings">高級配置</string>
|
||||||
<string name="appearance_settings">外觀配置</string>
|
<string name="appearance_settings">外觀配置</string>
|
||||||
<string name="back">返回</string>
|
<string name="back">返回</string>
|
||||||
|
<string name="susfs_enabled">SuSFS 已啟用</string>
|
||||||
|
<string name="susfs_disabled">SuSFS 已禁用</string>
|
||||||
<string name="background_set_success">背景設定成功</string>
|
<string name="background_set_success">背景設定成功</string>
|
||||||
<string name="background_removed">已移除自定義背景</string>
|
<string name="background_removed">已移除自定義背景</string>
|
||||||
<string name="icon_switch_title">備選圖標</string>
|
<string name="icon_switch_title">備選圖標</string>
|
||||||
|
|
@ -573,6 +576,7 @@
|
||||||
<string name="home_zygisk_implement">Zygisk 實現</string>
|
<string name="home_zygisk_implement">Zygisk 實現</string>
|
||||||
<!-- 循环路径相关 -->
|
<!-- 循环路径相关 -->
|
||||||
<!-- 循环路径功能描述 -->
|
<!-- 循环路径功能描述 -->
|
||||||
|
<string name="sus_loop_path_feature_label">SuS 循環路徑</string>
|
||||||
<string name="sus_loop_paths_description_title">循環路徑配置</string>
|
<string name="sus_loop_paths_description_title">循環路徑配置</string>
|
||||||
<string name="sus_loop_paths_description_text">循環路徑會喺每次非 root 用戶應用程式或者隔離服務啟動時,重新標記做 SUS_PATH。咁樣可以解決因為 inode 狀態重設或者核心重新建立 inode 而令到添加嘅路徑失效嘅問題。</string>
|
<string name="sus_loop_paths_description_text">循環路徑會喺每次非 root 用戶應用程式或者隔離服務啟動時,重新標記做 SUS_PATH。咁樣可以解決因為 inode 狀態重設或者核心重新建立 inode 而令到添加嘅路徑失效嘅問題。</string>
|
||||||
<string name="avc_log_spoofing">AVC 日誌欺騙</string>
|
<string name="avc_log_spoofing">AVC 日誌欺騙</string>
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,7 @@
|
||||||
<string name="install_inactive_slot_warning">將在重新啟動後強制切換至另一槽位!\n注意:僅能在 OTA 更新完成後重新啟動前使用。\n確定繼續?</string>
|
<string name="install_inactive_slot_warning">將在重新啟動後強制切換至另一槽位!\n注意:僅能在 OTA 更新完成後重新啟動前使用。\n確定繼續?</string>
|
||||||
<string name="install_next">下一步</string>
|
<string name="install_next">下一步</string>
|
||||||
<string name="select_file_tip">建議選擇 %1$s 分區映像檔</string>
|
<string name="select_file_tip">建議選擇 %1$s 分區映像檔</string>
|
||||||
|
<string name="select_file_tip_vendor">(實驗性的)</string>
|
||||||
<string name="select_kmi">選擇 KMI</string>
|
<string name="select_kmi">選擇 KMI</string>
|
||||||
<string name="settings_uninstall">解除安裝</string>
|
<string name="settings_uninstall">解除安裝</string>
|
||||||
<string name="settings_uninstall_temporary">臨時解除安裝</string>
|
<string name="settings_uninstall_temporary">臨時解除安裝</string>
|
||||||
|
|
@ -124,6 +125,7 @@
|
||||||
<string name="selected_lkm">已選擇的 LKM:%s</string>
|
<string name="selected_lkm">已選擇的 LKM:%s</string>
|
||||||
<string name="save_log">儲存日誌</string>
|
<string name="save_log">儲存日誌</string>
|
||||||
<string name="log_saved">日誌已儲存</string>
|
<string name="log_saved">日誌已儲存</string>
|
||||||
|
<string name="sus_su_mode">SuS SU 模式:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">確定安裝模組 %1$s?</string>
|
<string name="module_install_confirm">確定安裝模組 %1$s?</string>
|
||||||
<string name="unknown_module">未知模組</string>
|
<string name="unknown_module">未知模組</string>
|
||||||
|
|
@ -277,6 +279,8 @@
|
||||||
<string name="advanced_settings">進階設定</string>
|
<string name="advanced_settings">進階設定</string>
|
||||||
<string name="appearance_settings">外觀設定</string>
|
<string name="appearance_settings">外觀設定</string>
|
||||||
<string name="back">返回</string>
|
<string name="back">返回</string>
|
||||||
|
<string name="susfs_enabled">SuSFS 已啟用</string>
|
||||||
|
<string name="susfs_disabled">SuSFS 已禁用</string>
|
||||||
<string name="background_set_success">背景設定成功</string>
|
<string name="background_set_success">背景設定成功</string>
|
||||||
<string name="background_removed">已移除自訂背景</string>
|
<string name="background_removed">已移除自訂背景</string>
|
||||||
<string name="icon_switch_title">備用圖示</string>
|
<string name="icon_switch_title">備用圖示</string>
|
||||||
|
|
@ -589,6 +593,7 @@
|
||||||
<string name="loop_paths_section">循環路徑</string>
|
<string name="loop_paths_section">循環路徑</string>
|
||||||
<string name="add_loop_path">新增循環路徑</string>
|
<string name="add_loop_path">新增循環路徑</string>
|
||||||
<!-- 循环路径功能描述 -->
|
<!-- 循环路径功能描述 -->
|
||||||
|
<string name="sus_loop_path_feature_label">SuS 循環路徑</string>
|
||||||
<string name="sus_loop_paths_description_title">循環路徑設定</string>
|
<string name="sus_loop_paths_description_title">循環路徑設定</string>
|
||||||
<string name="sus_loop_paths_description_text">循環路徑會在每次非 root 使用者應用程式或隔離服務啟動時重新標記為 SUS_PATH。這有助於解決新增的路徑可能因 inode 狀態重設或內核中 inode 重新建立而失效的問題</string>
|
<string name="sus_loop_paths_description_text">循環路徑會在每次非 root 使用者應用程式或隔離服務啟動時重新標記為 SUS_PATH。這有助於解決新增的路徑可能因 inode 狀態重設或內核中 inode 重新建立而失效的問題</string>
|
||||||
<string name="avc_log_spoofing">AVC 日誌偽裝</string>
|
<string name="avc_log_spoofing">AVC 日誌偽裝</string>
|
||||||
|
|
@ -642,6 +647,7 @@
|
||||||
<string name="uid_multi_user_scan_title">多使用者應用掃描</string>
|
<string name="uid_multi_user_scan_title">多使用者應用掃描</string>
|
||||||
<string name="uid_multi_user_scan_summary">開啟後將掃描所有使用者的應用,包括工作資料等</string>
|
<string name="uid_multi_user_scan_summary">開啟後將掃描所有使用者的應用,包括工作資料等</string>
|
||||||
<string name="uid_scanner_setting_failed">設定失敗,請檢查許可權</string>
|
<string name="uid_scanner_setting_failed">設定失敗,請檢查許可權</string>
|
||||||
|
<string name="uid_scanner_setting_error">設定失敗: %s</string>
|
||||||
<string name="clean_runtime_environment">清理執行環境</string>
|
<string name="clean_runtime_environment">清理執行環境</string>
|
||||||
<string name="clean_runtime_environment_summary">清理執行時檔案並停止掃描服務</string>
|
<string name="clean_runtime_environment_summary">清理執行時檔案並停止掃描服務</string>
|
||||||
<string name="clean_runtime_environment_confirm">您確定要清理執行環境嗎?這將停止掃描服務並刪除相關檔案</string>
|
<string name="clean_runtime_environment_confirm">您確定要清理執行環境嗎?這將停止掃描服務並刪除相關檔案</string>
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@
|
||||||
<string name="install_upload_lkm_file">Use local LKM file</string>
|
<string name="install_upload_lkm_file">Use local LKM file</string>
|
||||||
<string name="install_only_support_ko_file">Only .ko files are supported</string>
|
<string name="install_only_support_ko_file">Only .ko files are supported</string>
|
||||||
<string name="select_file_tip">%1$s partition image is recommended</string>
|
<string name="select_file_tip">%1$s partition image is recommended</string>
|
||||||
|
<string name="select_file_tip_vendor">(Unstable)</string>
|
||||||
<string name="select_kmi">Select KMI</string>
|
<string name="select_kmi">Select KMI</string>
|
||||||
<string name="settings_uninstall">Uninstall</string>
|
<string name="settings_uninstall">Uninstall</string>
|
||||||
<string name="settings_uninstall_temporary">Uninstall temporarily</string>
|
<string name="settings_uninstall_temporary">Uninstall temporarily</string>
|
||||||
|
|
@ -129,6 +130,7 @@
|
||||||
<string name="selected_lkm">Selected LKM: %s</string>
|
<string name="selected_lkm">Selected LKM: %s</string>
|
||||||
<string name="save_log">Save logs</string>
|
<string name="save_log">Save logs</string>
|
||||||
<string name="log_saved">Logs saved</string>
|
<string name="log_saved">Logs saved</string>
|
||||||
|
<string name="sus_su_mode">SuS SU mode:</string>
|
||||||
<!-- Module related -->
|
<!-- Module related -->
|
||||||
<string name="module_install_confirm">Confirm install module %1$s?</string>
|
<string name="module_install_confirm">Confirm install module %1$s?</string>
|
||||||
<string name="unknown_module">Unknown module</string>
|
<string name="unknown_module">Unknown module</string>
|
||||||
|
|
@ -170,8 +172,6 @@
|
||||||
<string name="settings_disable_kernel_umount_summary">Disable kernel-level umount behavior controlled by KernelSU.</string>
|
<string name="settings_disable_kernel_umount_summary">Disable kernel-level umount behavior controlled by KernelSU.</string>
|
||||||
<string name="settings_enable_enhanced_security">Enable enhanced security</string>
|
<string name="settings_enable_enhanced_security">Enable enhanced security</string>
|
||||||
<string name="settings_enable_enhanced_security_summary">Enable stricter security policies.</string>
|
<string name="settings_enable_enhanced_security_summary">Enable stricter security policies.</string>
|
||||||
<string name="feature_status_unsupported_summary">Kernel does not support this feature.</string>
|
|
||||||
<string name="feature_status_managed_summary">This feature is managed by a module.</string>
|
|
||||||
<string name="settings_mode_default">Default</string>
|
<string name="settings_mode_default">Default</string>
|
||||||
<string name="settings_mode_temp_enable">Temporarily enable</string>
|
<string name="settings_mode_temp_enable">Temporarily enable</string>
|
||||||
<string name="settings_mode_always_enable">Permanently enable</string>
|
<string name="settings_mode_always_enable">Permanently enable</string>
|
||||||
|
|
@ -292,6 +292,8 @@
|
||||||
<string name="advanced_settings">Advanced Settings</string>
|
<string name="advanced_settings">Advanced Settings</string>
|
||||||
<string name="appearance_settings">Customize the toolbar</string>
|
<string name="appearance_settings">Customize the toolbar</string>
|
||||||
<string name="back">Comeback</string>
|
<string name="back">Comeback</string>
|
||||||
|
<string name="susfs_enabled">SuSFS enabled</string>
|
||||||
|
<string name="susfs_disabled">SuSFS disabled</string>
|
||||||
<string name="background_set_success">Background set successfully</string>
|
<string name="background_set_success">Background set successfully</string>
|
||||||
<string name="background_removed">Removed custom backgrounds</string>
|
<string name="background_removed">Removed custom backgrounds</string>
|
||||||
<string name="icon_switch_title">Alternate icon</string>
|
<string name="icon_switch_title">Alternate icon</string>
|
||||||
|
|
@ -608,6 +610,7 @@
|
||||||
<string name="loop_paths_section">Loop Paths</string>
|
<string name="loop_paths_section">Loop Paths</string>
|
||||||
<string name="add_loop_path">Add Loop Path</string>
|
<string name="add_loop_path">Add Loop Path</string>
|
||||||
<!-- 循环路径功能描述 -->
|
<!-- 循环路径功能描述 -->
|
||||||
|
<string name="sus_loop_path_feature_label">SUS Loop Path</string>
|
||||||
<string name="sus_loop_paths_description_title">Loop Path Configuration</string>
|
<string name="sus_loop_paths_description_title">Loop Path Configuration</string>
|
||||||
<string name="sus_loop_paths_description_text">Loop paths are re-flagged as SUS_PATH on each non-root user app or isolated service startup. This helps address issues where added paths may have their inode status reset or inode re-created in the kernel</string>
|
<string name="sus_loop_paths_description_text">Loop paths are re-flagged as SUS_PATH on each non-root user app or isolated service startup. This helps address issues where added paths may have their inode status reset or inode re-created in the kernel</string>
|
||||||
<string name="avc_log_spoofing">AVC Log Spoofing</string>
|
<string name="avc_log_spoofing">AVC Log Spoofing</string>
|
||||||
|
|
@ -665,6 +668,7 @@ Important Note:\n
|
||||||
<string name="uid_multi_user_scan_title">Multi-User Application Scanning</string>
|
<string name="uid_multi_user_scan_title">Multi-User Application Scanning</string>
|
||||||
<string name="uid_multi_user_scan_summary">When enabled, scans applications for all users, including work profiles</string>
|
<string name="uid_multi_user_scan_summary">When enabled, scans applications for all users, including work profiles</string>
|
||||||
<string name="uid_scanner_setting_failed">Setting failed, please check permissions</string>
|
<string name="uid_scanner_setting_failed">Setting failed, please check permissions</string>
|
||||||
|
<string name="uid_scanner_setting_error">Setting failed: %s</string>
|
||||||
<string name="clean_runtime_environment">Clean Runtime Environment</string>
|
<string name="clean_runtime_environment">Clean Runtime Environment</string>
|
||||||
<string name="clean_runtime_environment_summary">Clean up runtime files and stop the scanner service</string>
|
<string name="clean_runtime_environment_summary">Clean up runtime files and stop the scanner service</string>
|
||||||
<string name="clean_runtime_environment_confirm">Are you sure you want to clean the runtime environment? This will stop the scanner service and remove related files.</string>
|
<string name="clean_runtime_environment_confirm">Are you sure you want to clean the runtime environment? This will stop the scanner service and remove related files.</string>
|
||||||
|
|
@ -707,6 +711,9 @@ Important Note:\n
|
||||||
<string name="log_viewer_clear_logs">Clear Logs</string>
|
<string name="log_viewer_clear_logs">Clear Logs</string>
|
||||||
<string name="log_viewer_clear_logs_confirm">Are you sure you want to clear the selected log file? This action cannot be undone.</string>
|
<string name="log_viewer_clear_logs_confirm">Are you sure you want to clear the selected log file? This action cannot be undone.</string>
|
||||||
<string name="log_viewer_logs_cleared">Logs cleared successfully</string>
|
<string name="log_viewer_logs_cleared">Logs cleared successfully</string>
|
||||||
|
<string name="log_viewer_select_file">Select Log File</string>
|
||||||
|
<string name="log_viewer_current_log">Current Log</string>
|
||||||
|
<string name="log_viewer_old_log">Old Log</string>
|
||||||
<string name="log_viewer_filter_type">Filter by Type</string>
|
<string name="log_viewer_filter_type">Filter by Type</string>
|
||||||
<string name="log_viewer_all_types">All Types</string>
|
<string name="log_viewer_all_types">All Types</string>
|
||||||
<string name="log_viewer_showing_entries">Showing %1$d of %2$d entries</string>
|
<string name="log_viewer_showing_entries">Showing %1$d of %2$d entries</string>
|
||||||
|
|
@ -735,6 +742,8 @@ Important Note:\n
|
||||||
<string name="umount_path_restart_notice">A reboot is required for changes to take effect. The system will apply the new configuration on the next boot.</string>
|
<string name="umount_path_restart_notice">A reboot is required for changes to take effect. The system will apply the new configuration on the next boot.</string>
|
||||||
<string name="add_umount_path">Add Umount Path</string>
|
<string name="add_umount_path">Add Umount Path</string>
|
||||||
<string name="mount_path">Mount Path</string>
|
<string name="mount_path">Mount Path</string>
|
||||||
|
<string name="check_mount_type">Check Mount Type</string>
|
||||||
|
<string name="check_mount_type_overlay">Check if it is an overlay type</string>
|
||||||
<string name="umount_flags">Unmount Flags</string>
|
<string name="umount_flags">Unmount Flags</string>
|
||||||
<string name="umount_flags_hint">0=Normal unmount, 8=MNT_DETACH, -1=Auto</string>
|
<string name="umount_flags_hint">0=Normal unmount, 8=MNT_DETACH, -1=Auto</string>
|
||||||
<string name="flags">Flags</string>
|
<string name="flags">Flags</string>
|
||||||
|
|
@ -752,6 +761,4 @@ Important Note:\n
|
||||||
<string name="config_applied">Configuration applied to kernel</string>
|
<string name="config_applied">Configuration applied to kernel</string>
|
||||||
<string name="mnt_detach">MNT_DETACH</string>
|
<string name="mnt_detach">MNT_DETACH</string>
|
||||||
<string name="group_contains_apps">Contains %d apps</string>
|
<string name="group_contains_apps">Contains %d apps</string>
|
||||||
<string name="settings_disable_sulog">Disable superuser logging</string>
|
|
||||||
<string name="settings_disable_sulog_summary">Disable KernelSU superuser access logging</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
872
userspace/ksud/Cargo.lock
generated
872
userspace/ksud/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -6,11 +6,11 @@ edition = "2024"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
notify = "8.2"
|
notify = "6.1"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
const_format = "0.2"
|
const_format = "0.2"
|
||||||
zip = { version = "6", features = [
|
zip = { version = "3", features = [
|
||||||
"deflate",
|
"deflate",
|
||||||
"deflate64",
|
"deflate64",
|
||||||
"time",
|
"time",
|
||||||
|
|
@ -38,7 +38,7 @@ rust-embed = { version = "8", features = [
|
||||||
"debug-embed",
|
"debug-embed",
|
||||||
"compression", # must clean build after updating binaries
|
"compression", # must clean build after updating binaries
|
||||||
] }
|
] }
|
||||||
which = "8"
|
which = "7"
|
||||||
getopts = "0.2"
|
getopts = "0.2"
|
||||||
sha256 = "1"
|
sha256 = "1"
|
||||||
sha1 = "0.10"
|
sha1 = "0.10"
|
||||||
|
|
@ -48,14 +48,14 @@ regex-lite = "0.1"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies]
|
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies]
|
||||||
rustix = { git = "https://github.com/Kernel-SU/rustix.git", rev = "4a53fbc7cb7a07cabe87125cc21dbc27db316259", features = [
|
rustix = { git = "https://github.com/Kernel-SU/rustix.git", branch = "main", features = [
|
||||||
"all-apis",
|
"all-apis",
|
||||||
] }
|
] }
|
||||||
# some android specific dependencies which compiles under unix are also listed here for convenience of coding
|
# some android specific dependencies which compiles under unix are also listed here for convenience of coding
|
||||||
android-properties = { version = "0.2", features = ["bionic-deprecated"] }
|
android-properties = { version = "0.2", features = ["bionic-deprecated"] }
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
android_logger = { version = "0.15", default-features = false }
|
android_logger = { version = "0.14", default-features = false }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
overflow-checks = false
|
overflow-checks = false
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,13 +1,15 @@
|
||||||
use anyhow::{Ok, Result};
|
use anyhow::{Ok, Result};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
use android_logger::Config;
|
use android_logger::Config;
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
|
|
||||||
use crate::{apk_sign, assets, debug, defs, init_event, ksucalls, module, utils};
|
use crate::{
|
||||||
|
apk_sign, assets, debug, defs, defs::KSUD_VERBOSE_LOG_FILE, init_event, ksucalls, module, utils,
|
||||||
|
};
|
||||||
|
|
||||||
/// KernelSU userspace cli
|
/// KernelSU userspace cli
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
|
|
@ -15,6 +17,9 @@ use crate::{apk_sign, assets, debug, defs, init_event, ksucalls, module, utils};
|
||||||
struct Args {
|
struct Args {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
command: Commands,
|
command: Commands,
|
||||||
|
|
||||||
|
#[arg(short, long, default_value_t = cfg!(debug_assertions))]
|
||||||
|
verbose: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(clap::Subcommand, Debug)]
|
#[derive(clap::Subcommand, Debug)]
|
||||||
|
|
@ -147,11 +152,6 @@ enum Commands {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
command: Debug,
|
command: Debug,
|
||||||
},
|
},
|
||||||
/// Kernel interface
|
|
||||||
Kernel {
|
|
||||||
#[command(subcommand)]
|
|
||||||
command: Kernel,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(clap::Subcommand, Debug)]
|
#[derive(clap::Subcommand, Debug)]
|
||||||
|
|
@ -184,7 +184,7 @@ enum Debug {
|
||||||
/// Set the manager app, kernel CONFIG_KSU_DEBUG should be enabled.
|
/// Set the manager app, kernel CONFIG_KSU_DEBUG should be enabled.
|
||||||
SetManager {
|
SetManager {
|
||||||
/// manager package name
|
/// manager package name
|
||||||
#[arg(default_value_t = String::from("com.sukisu.ultra"))]
|
#[arg(default_value_t = String::from("me.weishu.kernelsu"))]
|
||||||
apk: String,
|
apk: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -204,6 +204,8 @@ enum Debug {
|
||||||
/// Get kernel version
|
/// Get kernel version
|
||||||
Version,
|
Version,
|
||||||
|
|
||||||
|
Mount,
|
||||||
|
|
||||||
/// For testing
|
/// For testing
|
||||||
Test,
|
Test,
|
||||||
|
|
||||||
|
|
@ -270,14 +272,14 @@ enum Module {
|
||||||
zip: String,
|
zip: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Undo module uninstall mark <id>
|
/// Uninstall module <id>
|
||||||
UndoUninstall {
|
Uninstall {
|
||||||
/// module id
|
/// module id
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Uninstall module <id>
|
/// Restore module <id>
|
||||||
Uninstall {
|
Restore {
|
||||||
/// module id
|
/// module id
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
|
|
@ -302,51 +304,6 @@ enum Module {
|
||||||
|
|
||||||
/// list all modules
|
/// list all modules
|
||||||
List,
|
List,
|
||||||
|
|
||||||
/// manage module configuration
|
|
||||||
Config {
|
|
||||||
#[command(subcommand)]
|
|
||||||
command: ModuleConfigCmd,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(clap::Subcommand, Debug)]
|
|
||||||
enum ModuleConfigCmd {
|
|
||||||
/// Get a config value
|
|
||||||
Get {
|
|
||||||
/// config key
|
|
||||||
key: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Set a config value
|
|
||||||
Set {
|
|
||||||
/// config key
|
|
||||||
key: String,
|
|
||||||
/// config value
|
|
||||||
value: String,
|
|
||||||
/// use temporary config (cleared on reboot)
|
|
||||||
#[arg(short, long)]
|
|
||||||
temp: bool,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// List all config entries
|
|
||||||
List,
|
|
||||||
|
|
||||||
/// Delete a config entry
|
|
||||||
Delete {
|
|
||||||
/// config key
|
|
||||||
key: String,
|
|
||||||
/// delete from temporary config
|
|
||||||
#[arg(short, long)]
|
|
||||||
temp: bool,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Clear all config entries
|
|
||||||
Clear {
|
|
||||||
/// clear temporary config
|
|
||||||
#[arg(short, long)]
|
|
||||||
temp: bool,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(clap::Subcommand, Debug)]
|
#[derive(clap::Subcommand, Debug)]
|
||||||
|
|
@ -421,41 +378,6 @@ enum Feature {
|
||||||
Save,
|
Save,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(clap::Subcommand, Debug)]
|
|
||||||
enum Kernel {
|
|
||||||
/// Nuke ext4 sysfs
|
|
||||||
NukeExt4Sysfs {
|
|
||||||
/// mount point
|
|
||||||
mnt: String,
|
|
||||||
},
|
|
||||||
/// Manage umount list
|
|
||||||
Umount {
|
|
||||||
#[command(subcommand)]
|
|
||||||
command: UmountOp,
|
|
||||||
},
|
|
||||||
/// Notify that module is mounted
|
|
||||||
NotifyModuleMounted,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(clap::Subcommand, Debug)]
|
|
||||||
enum UmountOp {
|
|
||||||
/// Add mount point to umount list
|
|
||||||
Add {
|
|
||||||
/// mount point path
|
|
||||||
mnt: String,
|
|
||||||
/// umount flags (default: 0, MNT_DETACH: 2)
|
|
||||||
#[arg(short, long, default_value = "0")]
|
|
||||||
flags: u32,
|
|
||||||
},
|
|
||||||
/// Delete mount point from umount list
|
|
||||||
Del {
|
|
||||||
/// mount point path
|
|
||||||
mnt: String,
|
|
||||||
},
|
|
||||||
/// Wipe all entries from umount list
|
|
||||||
Wipe,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
mod kpm_cmd {
|
mod kpm_cmd {
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
|
|
@ -489,6 +411,7 @@ enum Umount {
|
||||||
|
|
||||||
/// Check mount type (overlay)
|
/// Check mount type (overlay)
|
||||||
#[arg(long, default_value = "false")]
|
#[arg(long, default_value = "false")]
|
||||||
|
check_mnt: bool,
|
||||||
|
|
||||||
/// Umount flags (0 or 8 for MNT_DETACH)
|
/// Umount flags (0 or 8 for MNT_DETACH)
|
||||||
#[arg(long, default_value = "-1")]
|
#[arg(long, default_value = "-1")]
|
||||||
|
|
@ -504,7 +427,7 @@ enum Umount {
|
||||||
/// List all umount paths
|
/// List all umount paths
|
||||||
List,
|
List,
|
||||||
|
|
||||||
/// Clear all recorded umount paths
|
/// Clear all custom paths (keep defaults)
|
||||||
ClearCustom,
|
ClearCustom,
|
||||||
|
|
||||||
/// Save configuration to file
|
/// Save configuration to file
|
||||||
|
|
@ -536,6 +459,10 @@ pub fn run() -> Result<()> {
|
||||||
|
|
||||||
let cli = Args::parse();
|
let cli = Args::parse();
|
||||||
|
|
||||||
|
if !cli.verbose && !Path::new(KSUD_VERBOSE_LOG_FILE).exists() {
|
||||||
|
log::set_max_level(LevelFilter::Info);
|
||||||
|
}
|
||||||
|
|
||||||
log::info!("command: {:?}", cli.command);
|
log::info!("command: {:?}", cli.command);
|
||||||
|
|
||||||
let result = match cli.command {
|
let result = match cli.command {
|
||||||
|
|
@ -549,72 +476,12 @@ pub fn run() -> Result<()> {
|
||||||
}
|
}
|
||||||
match command {
|
match command {
|
||||||
Module::Install { zip } => module::install_module(&zip),
|
Module::Install { zip } => module::install_module(&zip),
|
||||||
Module::UndoUninstall { id } => module::undo_uninstall_module(&id),
|
|
||||||
Module::Uninstall { id } => module::uninstall_module(&id),
|
Module::Uninstall { id } => module::uninstall_module(&id),
|
||||||
|
Module::Restore { id } => module::restore_uninstall_module(&id),
|
||||||
Module::Enable { id } => module::enable_module(&id),
|
Module::Enable { id } => module::enable_module(&id),
|
||||||
Module::Disable { id } => module::disable_module(&id),
|
Module::Disable { id } => module::disable_module(&id),
|
||||||
Module::Action { id } => module::run_action(&id),
|
Module::Action { id } => module::run_action(&id),
|
||||||
Module::List => module::list_modules(),
|
Module::List => module::list_modules(),
|
||||||
Module::Config { command } => {
|
|
||||||
// Get module ID from environment variable
|
|
||||||
let module_id = std::env::var("KSU_MODULE").map_err(|_| {
|
|
||||||
anyhow::anyhow!("This command must be run in the context of a module")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
use crate::module_config;
|
|
||||||
match command {
|
|
||||||
ModuleConfigCmd::Get { key } => {
|
|
||||||
// Use merge_configs to respect priority (temp overrides persist)
|
|
||||||
let config = module_config::merge_configs(&module_id)?;
|
|
||||||
match config.get(&key) {
|
|
||||||
Some(value) => {
|
|
||||||
println!("{}", value);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
None => anyhow::bail!("Key '{}' not found", key),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ModuleConfigCmd::Set { key, value, temp } => {
|
|
||||||
// Validate input at CLI layer for better user experience
|
|
||||||
module_config::validate_config_key(&key)?;
|
|
||||||
module_config::validate_config_value(&value)?;
|
|
||||||
|
|
||||||
let config_type = if temp {
|
|
||||||
module_config::ConfigType::Temp
|
|
||||||
} else {
|
|
||||||
module_config::ConfigType::Persist
|
|
||||||
};
|
|
||||||
module_config::set_config_value(&module_id, &key, &value, config_type)
|
|
||||||
}
|
|
||||||
ModuleConfigCmd::List => {
|
|
||||||
let config = module_config::merge_configs(&module_id)?;
|
|
||||||
if config.is_empty() {
|
|
||||||
println!("No config entries found");
|
|
||||||
} else {
|
|
||||||
for (key, value) in config {
|
|
||||||
println!("{}={}", key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
ModuleConfigCmd::Delete { key, temp } => {
|
|
||||||
let config_type = if temp {
|
|
||||||
module_config::ConfigType::Temp
|
|
||||||
} else {
|
|
||||||
module_config::ConfigType::Persist
|
|
||||||
};
|
|
||||||
module_config::delete_config_value(&module_id, &key, config_type)
|
|
||||||
}
|
|
||||||
ModuleConfigCmd::Clear { temp } => {
|
|
||||||
let config_type = if temp {
|
|
||||||
module_config::ConfigType::Temp
|
|
||||||
} else {
|
|
||||||
module_config::ConfigType::Persist
|
|
||||||
};
|
|
||||||
module_config::clear_config(&module_id, config_type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Commands::Install { magiskboot } => utils::install(magiskboot),
|
Commands::Install { magiskboot } => utils::install(magiskboot),
|
||||||
|
|
@ -657,6 +524,7 @@ pub fn run() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Debug::Su { global_mnt } => crate::su::grant_root(global_mnt),
|
Debug::Su { global_mnt } => crate::su::grant_root(global_mnt),
|
||||||
|
Debug::Mount => init_event::mount_modules_systemlessly(),
|
||||||
Debug::Test => assets::ensure_binaries(false),
|
Debug::Test => assets::ensure_binaries(false),
|
||||||
Debug::Mark { command } => match command {
|
Debug::Mark { command } => match command {
|
||||||
MarkCommand::Get { pid } => debug::mark_get(pid),
|
MarkCommand::Get { pid } => debug::mark_get(pid),
|
||||||
|
|
@ -722,18 +590,6 @@ pub fn run() -> Result<()> {
|
||||||
magiskboot,
|
magiskboot,
|
||||||
flash,
|
flash,
|
||||||
} => crate::boot_patch::restore(boot, magiskboot, flash),
|
} => crate::boot_patch::restore(boot, magiskboot, flash),
|
||||||
Commands::Kernel { command } => match command {
|
|
||||||
Kernel::NukeExt4Sysfs { mnt } => ksucalls::nuke_ext4_sysfs(&mnt),
|
|
||||||
Kernel::Umount { command } => match command {
|
|
||||||
UmountOp::Add { mnt, flags } => ksucalls::umount_list_add(&mnt, flags),
|
|
||||||
UmountOp::Del { mnt } => ksucalls::umount_list_del(&mnt),
|
|
||||||
UmountOp::Wipe => ksucalls::umount_list_wipe().map_err(Into::into),
|
|
||||||
},
|
|
||||||
Kernel::NotifyModuleMounted => {
|
|
||||||
ksucalls::report_module_mounted();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
Commands::Kpm { command } => {
|
Commands::Kpm { command } => {
|
||||||
use crate::cli::kpm_cmd::Kpm;
|
use crate::cli::kpm_cmd::Kpm;
|
||||||
|
|
@ -754,7 +610,11 @@ pub fn run() -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Commands::Umount { command } => match command {
|
Commands::Umount { command } => match command {
|
||||||
Umount::Add { path, flags } => crate::umount_manager::add_umount_path(&path, flags),
|
Umount::Add {
|
||||||
|
path,
|
||||||
|
check_mnt,
|
||||||
|
flags,
|
||||||
|
} => crate::umount_manager::add_umount_path(&path, check_mnt, flags),
|
||||||
Umount::Remove { path } => crate::umount_manager::remove_umount_path(&path),
|
Umount::Remove { path } => crate::umount_manager::remove_umount_path(&path),
|
||||||
Umount::List => crate::umount_manager::list_umount_paths(),
|
Umount::List => crate::umount_manager::list_umount_paths(),
|
||||||
Umount::ClearCustom => crate::umount_manager::clear_custom_paths(),
|
Umount::ClearCustom => crate::umount_manager::clear_custom_paths(),
|
||||||
|
|
@ -765,7 +625,11 @@ pub fn run() -> Result<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = &result {
|
if let Err(e) = &result {
|
||||||
log::error!("Error: {e:?}");
|
for c in e.chain() {
|
||||||
|
log::error!("{c:#?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
log::error!("{:#?}", e.backtrace());
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ pub const PROFILE_SELINUX_DIR: &str = concatcp!(PROFILE_DIR, "selinux/");
|
||||||
pub const PROFILE_TEMPLATE_DIR: &str = concatcp!(PROFILE_DIR, "templates/");
|
pub const PROFILE_TEMPLATE_DIR: &str = concatcp!(PROFILE_DIR, "templates/");
|
||||||
|
|
||||||
pub const KSURC_PATH: &str = concatcp!(WORKING_DIR, ".ksurc");
|
pub const KSURC_PATH: &str = concatcp!(WORKING_DIR, ".ksurc");
|
||||||
|
pub const KSU_MOUNT_SOURCE: &str = "KSU";
|
||||||
pub const DAEMON_PATH: &str = concatcp!(ADB_DIR, "ksud");
|
pub const DAEMON_PATH: &str = concatcp!(ADB_DIR, "ksud");
|
||||||
pub const MAGISKBOOT_PATH: &str = concatcp!(BINARY_DIR, "magiskboot");
|
pub const MAGISKBOOT_PATH: &str = concatcp!(BINARY_DIR, "magiskboot");
|
||||||
|
|
||||||
|
|
@ -17,24 +18,18 @@ pub const MAGISKBOOT_PATH: &str = concatcp!(BINARY_DIR, "magiskboot");
|
||||||
pub const DAEMON_LINK_PATH: &str = concatcp!(BINARY_DIR, "ksud");
|
pub const DAEMON_LINK_PATH: &str = concatcp!(BINARY_DIR, "ksud");
|
||||||
|
|
||||||
pub const MODULE_DIR: &str = concatcp!(ADB_DIR, "modules/");
|
pub const MODULE_DIR: &str = concatcp!(ADB_DIR, "modules/");
|
||||||
|
|
||||||
|
// warning: this directory should not change, or you need to change the code in module_installer.sh!!!
|
||||||
pub const MODULE_UPDATE_DIR: &str = concatcp!(ADB_DIR, "modules_update/");
|
pub const MODULE_UPDATE_DIR: &str = concatcp!(ADB_DIR, "modules_update/");
|
||||||
pub const METAMODULE_DIR: &str = concatcp!(ADB_DIR, "metamodule/");
|
|
||||||
|
pub const KSUD_VERBOSE_LOG_FILE: &str = concatcp!(ADB_DIR, "verbose");
|
||||||
|
|
||||||
pub const MODULE_WEB_DIR: &str = "webroot";
|
pub const MODULE_WEB_DIR: &str = "webroot";
|
||||||
pub const MODULE_ACTION_SH: &str = "action.sh";
|
pub const MODULE_ACTION_SH: &str = "action.sh";
|
||||||
pub const DISABLE_FILE_NAME: &str = "disable";
|
pub const DISABLE_FILE_NAME: &str = "disable";
|
||||||
pub const UPDATE_FILE_NAME: &str = "update";
|
pub const UPDATE_FILE_NAME: &str = "update";
|
||||||
pub const REMOVE_FILE_NAME: &str = "remove";
|
pub const REMOVE_FILE_NAME: &str = "remove";
|
||||||
|
pub const SKIP_MOUNT_FILE_NAME: &str = "skip_mount";
|
||||||
// Module config system
|
|
||||||
pub const MODULE_CONFIG_DIR: &str = concatcp!(WORKING_DIR, "module_configs/");
|
|
||||||
pub const PERSIST_CONFIG_NAME: &str = "persist.config";
|
|
||||||
pub const TEMP_CONFIG_NAME: &str = "tmp.config";
|
|
||||||
|
|
||||||
// Metamodule support
|
|
||||||
pub const METAMODULE_MOUNT_SCRIPT: &str = "metamount.sh";
|
|
||||||
pub const METAMODULE_METAINSTALL_SCRIPT: &str = "metainstall.sh";
|
|
||||||
pub const METAMODULE_METAUNINSTALL_SCRIPT: &str = "metauninstall.sh";
|
|
||||||
|
|
||||||
pub const VERSION_CODE: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_CODE"));
|
pub const VERSION_CODE: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_CODE"));
|
||||||
pub const VERSION_NAME: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_NAME"));
|
pub const VERSION_NAME: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_NAME"));
|
||||||
|
|
@ -42,3 +37,6 @@ pub const VERSION_NAME: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_N
|
||||||
pub const KSU_BACKUP_DIR: &str = WORKING_DIR;
|
pub const KSU_BACKUP_DIR: &str = WORKING_DIR;
|
||||||
pub const KSU_BACKUP_FILE_PREFIX: &str = "ksu_backup_";
|
pub const KSU_BACKUP_FILE_PREFIX: &str = "ksu_backup_";
|
||||||
pub const BACKUP_FILENAME: &str = "stock_image.sha1";
|
pub const BACKUP_FILENAME: &str = "stock_image.sha1";
|
||||||
|
|
||||||
|
pub const NO_TMPFS_PATH: &str = concatcp!(WORKING_DIR, ".notmpfs");
|
||||||
|
pub const NO_MOUNT_PATH: &str = concatcp!(WORKING_DIR, ".nomount");
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ pub enum FeatureId {
|
||||||
SuCompat = 0,
|
SuCompat = 0,
|
||||||
KernelUmount = 1,
|
KernelUmount = 1,
|
||||||
EnhancedSecurity = 2,
|
EnhancedSecurity = 2,
|
||||||
SuLog = 3,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FeatureId {
|
impl FeatureId {
|
||||||
|
|
@ -26,7 +25,6 @@ impl FeatureId {
|
||||||
0 => Some(FeatureId::SuCompat),
|
0 => Some(FeatureId::SuCompat),
|
||||||
1 => Some(FeatureId::KernelUmount),
|
1 => Some(FeatureId::KernelUmount),
|
||||||
2 => Some(FeatureId::EnhancedSecurity),
|
2 => Some(FeatureId::EnhancedSecurity),
|
||||||
3 => Some(FeatureId::SuLog),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +34,6 @@ impl FeatureId {
|
||||||
FeatureId::SuCompat => "su_compat",
|
FeatureId::SuCompat => "su_compat",
|
||||||
FeatureId::KernelUmount => "kernel_umount",
|
FeatureId::KernelUmount => "kernel_umount",
|
||||||
FeatureId::EnhancedSecurity => "enhanced_security",
|
FeatureId::EnhancedSecurity => "enhanced_security",
|
||||||
FeatureId::SuLog => "sulog",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,9 +48,6 @@ impl FeatureId {
|
||||||
FeatureId::EnhancedSecurity => {
|
FeatureId::EnhancedSecurity => {
|
||||||
"Enhanced Security - disable non‑KSU root elevation and unauthorized UID downgrades"
|
"Enhanced Security - disable non‑KSU root elevation and unauthorized UID downgrades"
|
||||||
}
|
}
|
||||||
FeatureId::SuLog => {
|
|
||||||
"SU Log - enables logging of SU command usage to kernel log for auditing purposes"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -63,7 +57,6 @@ fn parse_feature_id(name: &str) -> Result<FeatureId> {
|
||||||
"su_compat" | "0" => Ok(FeatureId::SuCompat),
|
"su_compat" | "0" => Ok(FeatureId::SuCompat),
|
||||||
"kernel_umount" | "1" => Ok(FeatureId::KernelUmount),
|
"kernel_umount" | "1" => Ok(FeatureId::KernelUmount),
|
||||||
"enhanced_security" | "2" => Ok(FeatureId::EnhancedSecurity),
|
"enhanced_security" | "2" => Ok(FeatureId::EnhancedSecurity),
|
||||||
"sulog" | "3" => Ok(FeatureId::SuLog),
|
|
||||||
_ => bail!("Unknown feature: {}", name),
|
_ => bail!("Unknown feature: {}", name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -206,39 +199,6 @@ pub fn get_feature(id: String) -> Result<()> {
|
||||||
pub fn set_feature(id: String, value: u64) -> Result<()> {
|
pub fn set_feature(id: String, value: u64) -> Result<()> {
|
||||||
let feature_id = parse_feature_id(&id)?;
|
let feature_id = parse_feature_id(&id)?;
|
||||||
|
|
||||||
// Check if this feature is managed by any module
|
|
||||||
if let Ok(managed_features_map) = crate::module::get_managed_features() {
|
|
||||||
// Find which modules manage this feature
|
|
||||||
let managing_modules: Vec<&String> = managed_features_map
|
|
||||||
.iter()
|
|
||||||
.filter(|(_, features)| features.iter().any(|f| f == feature_id.name()))
|
|
||||||
.map(|(module_id, _)| module_id)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if !managing_modules.is_empty() {
|
|
||||||
// Feature is managed, check if caller is an authorized module
|
|
||||||
let caller_module = std::env::var("KSU_MODULE").unwrap_or_default();
|
|
||||||
|
|
||||||
if caller_module.is_empty() || !managing_modules.contains(&&caller_module) {
|
|
||||||
bail!(
|
|
||||||
"Feature '{}' is managed by module(s): {}. Direct modification is not allowed.",
|
|
||||||
feature_id.name(),
|
|
||||||
managing_modules
|
|
||||||
.iter()
|
|
||||||
.map(|s| s.as_str())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", ")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
log::info!(
|
|
||||||
"Module '{}' is setting managed feature '{}'",
|
|
||||||
caller_module,
|
|
||||||
feature_id.name()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
crate::ksucalls::set_feature(feature_id as u32, value)
|
crate::ksucalls::set_feature(feature_id as u32, value)
|
||||||
.with_context(|| format!("Failed to set feature {} to {}", id, value))?;
|
.with_context(|| format!("Failed to set feature {} to {}", id, value))?;
|
||||||
|
|
||||||
|
|
@ -274,7 +234,6 @@ pub fn list_features() -> Result<()> {
|
||||||
FeatureId::SuCompat,
|
FeatureId::SuCompat,
|
||||||
FeatureId::KernelUmount,
|
FeatureId::KernelUmount,
|
||||||
FeatureId::EnhancedSecurity,
|
FeatureId::EnhancedSecurity,
|
||||||
FeatureId::SuLog,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
for feature_id in all_features.iter() {
|
for feature_id in all_features.iter() {
|
||||||
|
|
@ -338,7 +297,6 @@ pub fn save_config() -> Result<()> {
|
||||||
FeatureId::SuCompat,
|
FeatureId::SuCompat,
|
||||||
FeatureId::KernelUmount,
|
FeatureId::KernelUmount,
|
||||||
FeatureId::EnhancedSecurity,
|
FeatureId::EnhancedSecurity,
|
||||||
FeatureId::SuLog,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
for feature_id in all_features.iter() {
|
for feature_id in all_features.iter() {
|
||||||
|
|
@ -391,7 +349,7 @@ pub fn init_features() -> Result<()> {
|
||||||
|
|
||||||
let mut features = load_binary_config()?;
|
let mut features = load_binary_config()?;
|
||||||
|
|
||||||
// Get managed features from active modules and skip them during init
|
// Get managed features from active modules
|
||||||
if let Ok(managed_features_map) = crate::module::get_managed_features() {
|
if let Ok(managed_features_map) = crate::module::get_managed_features() {
|
||||||
if !managed_features_map.is_empty() {
|
if !managed_features_map.is_empty() {
|
||||||
log::info!(
|
log::info!(
|
||||||
|
|
@ -399,7 +357,7 @@ pub fn init_features() -> Result<()> {
|
||||||
managed_features_map.len()
|
managed_features_map.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Build a set of all managed feature IDs to skip
|
// Force override managed features to 0
|
||||||
for (module_id, feature_list) in managed_features_map.iter() {
|
for (module_id, feature_list) in managed_features_map.iter() {
|
||||||
log::info!(
|
log::info!(
|
||||||
"Module '{}' manages {} feature(s)",
|
"Module '{}' manages {} feature(s)",
|
||||||
|
|
@ -409,20 +367,12 @@ pub fn init_features() -> Result<()> {
|
||||||
for feature_name in feature_list {
|
for feature_name in feature_list {
|
||||||
if let Ok(feature_id) = parse_feature_id(feature_name) {
|
if let Ok(feature_id) = parse_feature_id(feature_name) {
|
||||||
let feature_id_u32 = feature_id as u32;
|
let feature_id_u32 = feature_id as u32;
|
||||||
// Remove managed features from config, let modules control them
|
log::info!(
|
||||||
if features.remove(&feature_id_u32).is_some() {
|
" - Force overriding managed feature '{}' to 0 (by module: {})",
|
||||||
log::info!(
|
feature_name,
|
||||||
" - Skipping managed feature '{}' (controlled by module: {})",
|
module_id
|
||||||
feature_name,
|
);
|
||||||
module_id
|
features.insert(feature_id_u32, 0);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
log::info!(
|
|
||||||
" - Feature '{}' is managed by module '{}', skipping",
|
|
||||||
feature_name,
|
|
||||||
module_id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
" - Unknown managed feature '{}' from module '{}', ignoring",
|
" - Unknown managed feature '{}' from module '{}', ignoring",
|
||||||
|
|
@ -446,9 +396,9 @@ pub fn init_features() -> Result<()> {
|
||||||
|
|
||||||
apply_config(&features)?;
|
apply_config(&features)?;
|
||||||
|
|
||||||
// Save the configuration (excluding managed features)
|
// Save the final configuration (including managed features forced to 0)
|
||||||
save_binary_config(&features)?;
|
save_binary_config(&features)?;
|
||||||
log::info!("Saved feature configuration to file");
|
log::info!("Saved final feature configuration to file");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,34 @@
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use crate::kpm;
|
use crate::kpm;
|
||||||
use crate::module::{handle_updated_modules, prune_modules};
|
|
||||||
use crate::utils::is_safe_mode;
|
use crate::utils::is_safe_mode;
|
||||||
use crate::{
|
use crate::{
|
||||||
assets, defs, ksucalls, metamodule, restorecon,
|
assets, defs,
|
||||||
utils::{self},
|
defs::{KSU_MOUNT_SOURCE, NO_MOUNT_PATH, NO_TMPFS_PATH},
|
||||||
|
ksucalls,
|
||||||
|
module::{handle_updated_modules, prune_modules},
|
||||||
|
restorecon, uid_scanner, utils,
|
||||||
|
utils::find_tmp_path,
|
||||||
};
|
};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
use rustix::fs::{MountFlags, mount};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
pub fn mount_modules_systemlessly() -> Result<()> {
|
||||||
|
crate::magic_mount::magic_mount(&find_tmp_path())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
|
pub fn mount_modules_systemlessly() -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn on_post_data_fs() -> Result<()> {
|
pub fn on_post_data_fs() -> Result<()> {
|
||||||
ksucalls::report_post_fs_data();
|
ksucalls::report_post_fs_data();
|
||||||
|
|
||||||
utils::umask(0);
|
utils::umask(0);
|
||||||
|
|
||||||
// Clear all temporary module configs early
|
|
||||||
if let Err(e) = crate::module_config::clear_all_temp_configs() {
|
|
||||||
warn!("clear temp configs failed: {e}");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let _ = catch_bootlog("logcat", vec!["logcat"]);
|
let _ = catch_bootlog("logcat", vec!["logcat"]);
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
|
@ -30,11 +39,9 @@ pub fn on_post_data_fs() -> Result<()> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let safe_mode = crate::utils::is_safe_mode();
|
let safe_mode = utils::is_safe_mode();
|
||||||
|
|
||||||
if safe_mode {
|
if safe_mode {
|
||||||
// we should still ensure module directory exists in safe mode
|
|
||||||
// because we may need to operate the module dir in safe mode
|
|
||||||
warn!("safe mode, skip common post-fs-data.d scripts");
|
warn!("safe mode, skip common post-fs-data.d scripts");
|
||||||
} else {
|
} else {
|
||||||
// Then exec common post-fs-data scripts
|
// Then exec common post-fs-data scripts
|
||||||
|
|
@ -43,18 +50,18 @@ pub fn on_post_data_fs() -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let module_dir = defs::MODULE_DIR;
|
|
||||||
|
|
||||||
assets::ensure_binaries(true).with_context(|| "Failed to extract bin assets")?;
|
assets::ensure_binaries(true).with_context(|| "Failed to extract bin assets")?;
|
||||||
|
|
||||||
// Start UID scanner daemon with highest priority
|
// Start UID scanner daemon with highest priority
|
||||||
crate::uid_scanner::start_uid_scanner_daemon()?;
|
uid_scanner::start_uid_scanner_daemon()?;
|
||||||
|
|
||||||
if is_safe_mode() {
|
if is_safe_mode() {
|
||||||
warn!("safe mode, skip load feature config");
|
warn!("safe mode, skip load feature config");
|
||||||
} else if let Err(e) = crate::umount_manager::load_and_apply_config() {
|
} else if let Err(e) = crate::umount_manager::load_and_apply_config() {
|
||||||
warn!("Failed to load umount config: {e}");
|
warn!("Failed to load umount config: {e}");
|
||||||
}
|
}
|
||||||
|
// tell kernel that we've mount the module, so that it can do some optimization
|
||||||
|
ksucalls::report_module_mounted();
|
||||||
|
|
||||||
// if we are in safe mode, we should disable all modules
|
// if we are in safe mode, we should disable all modules
|
||||||
if safe_mode {
|
if safe_mode {
|
||||||
|
|
@ -65,14 +72,14 @@ pub fn on_post_data_fs() -> Result<()> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = handle_updated_modules() {
|
|
||||||
warn!("handle updated modules failed: {e}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = prune_modules() {
|
if let Err(e) = prune_modules() {
|
||||||
warn!("prune modules failed: {e}");
|
warn!("prune modules failed: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Err(e) = handle_updated_modules() {
|
||||||
|
warn!("handle updated modules failed: {e}");
|
||||||
|
}
|
||||||
|
|
||||||
if let Err(e) = restorecon::restorecon() {
|
if let Err(e) = restorecon::restorecon() {
|
||||||
warn!("restorecon failed: {e}");
|
warn!("restorecon failed: {e}");
|
||||||
}
|
}
|
||||||
|
|
@ -103,9 +110,23 @@ pub fn on_post_data_fs() -> Result<()> {
|
||||||
warn!("KPM: Failed to load KPM modules: {e}");
|
warn!("KPM: Failed to load KPM modules: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute metamodule post-fs-data script first (priority)
|
let tmpfs_path = find_tmp_path();
|
||||||
if let Err(e) = metamodule::exec_stage_script("post-fs-data", true) {
|
// for compatibility
|
||||||
warn!("exec metamodule post-fs-data script failed: {e}");
|
let no_mount = Path::new(NO_TMPFS_PATH).exists() || Path::new(NO_MOUNT_PATH).exists();
|
||||||
|
|
||||||
|
// mount temp dir
|
||||||
|
if !no_mount {
|
||||||
|
if let Err(e) = mount(
|
||||||
|
KSU_MOUNT_SOURCE,
|
||||||
|
&tmpfs_path,
|
||||||
|
"tmpfs",
|
||||||
|
MountFlags::empty(),
|
||||||
|
"",
|
||||||
|
) {
|
||||||
|
warn!("do temp dir mount failed: {e}");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info!("no tmpfs requested");
|
||||||
}
|
}
|
||||||
|
|
||||||
// exec modules post-fs-data scripts
|
// exec modules post-fs-data scripts
|
||||||
|
|
@ -119,15 +140,18 @@ pub fn on_post_data_fs() -> Result<()> {
|
||||||
warn!("load system.prop failed: {e}");
|
warn!("load system.prop failed: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute metamodule mount script
|
// mount module systemlessly by magic mount
|
||||||
if let Err(e) = metamodule::exec_mount_script(module_dir) {
|
#[cfg(target_os = "android")]
|
||||||
warn!("execute metamodule mount failed: {e}");
|
if !no_mount {
|
||||||
|
if let Err(e) = crate::magic_mount::magic_mount(&tmpfs_path) {
|
||||||
|
warn!("do systemless mount failed: {e}");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info!("no mount requested");
|
||||||
}
|
}
|
||||||
|
|
||||||
run_stage("post-mount", true);
|
run_stage("post-mount", true);
|
||||||
|
|
||||||
std::env::set_current_dir("/").with_context(|| "failed to chdir to /")?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,13 +171,6 @@ fn run_stage(stage: &str, block: bool) {
|
||||||
if let Err(e) = crate::module::exec_common_scripts(&format!("{stage}.d"), block) {
|
if let Err(e) = crate::module::exec_common_scripts(&format!("{stage}.d"), block) {
|
||||||
warn!("Failed to exec common {stage} scripts: {e}");
|
warn!("Failed to exec common {stage} scripts: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute metamodule stage script first (priority)
|
|
||||||
if let Err(e) = metamodule::exec_stage_script(stage, block) {
|
|
||||||
warn!("Failed to exec metamodule {stage} script: {e}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// execute regular modules stage scripts
|
|
||||||
if let Err(e) = crate::module::exec_stage_script(stage, block) {
|
if let Err(e) = crate::module::exec_stage_script(stage, block) {
|
||||||
warn!("Failed to exec {stage} scripts: {e}");
|
warn!("Failed to exec {stage} scripts: {e}");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ setup_flashable() {
|
||||||
$BOOTMODE && return
|
$BOOTMODE && return
|
||||||
if [ -z $OUTFD ] || readlink /proc/$$/fd/$OUTFD | grep -q /tmp; then
|
if [ -z $OUTFD ] || readlink /proc/$$/fd/$OUTFD | grep -q /tmp; then
|
||||||
# We will have to manually find out OUTFD
|
# We will have to manually find out OUTFD
|
||||||
for FD in `ls /proc/$$/fd`; do
|
for FD in /proc/$$/fd/*; do
|
||||||
if readlink /proc/$$/fd/$FD | grep -q pipe; then
|
if readlink /proc/$$/fd/$FD | grep -q pipe; then
|
||||||
if ps | grep -v grep | grep -qE " 3 $FD |status_fd=$FD"; then
|
if ps | grep -v grep | grep -qE " 3 $FD |status_fd=$FD"; then
|
||||||
OUTFD=$FD
|
OUTFD=$FD
|
||||||
|
|
@ -313,6 +313,14 @@ mark_remove() {
|
||||||
chmod 644 $1
|
chmod 644 $1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mark_replace() {
|
||||||
|
# REPLACE must be directory!!!
|
||||||
|
# https://docs.kernel.org/filesystems/overlayfs.html#whiteouts-and-opaque-directories
|
||||||
|
mkdir -p $1 2>/dev/null
|
||||||
|
setfattr -n trusted.overlay.opaque -v y $1
|
||||||
|
chmod 644 $1
|
||||||
|
}
|
||||||
|
|
||||||
request_size_check() {
|
request_size_check() {
|
||||||
reqSizeM=`du -ms "$1" | cut -f1`
|
reqSizeM=`du -ms "$1" | cut -f1`
|
||||||
}
|
}
|
||||||
|
|
@ -330,19 +338,16 @@ is_legacy_script() {
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_partition() {
|
handle_partition() {
|
||||||
# if /system/vendor is a symlink, we need to move it out of $MODPATH/system
|
PARTITION="$1"
|
||||||
# if /system/vendor is a normal directory, no special handling is needed.
|
REQUIRE_SYMLINK="$2"
|
||||||
if [ ! -e $MODPATH/system/$1 ]; then
|
if [ ! -e "$MODPATH/system/$PARTITION" ]; then
|
||||||
# no partition found
|
# no partition found
|
||||||
return;
|
return;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# we move the folder to / only if it is a native folder that is not a symlink
|
if [ "$REQUIRE_SYMLINK" = "false" ] || [ -L "/system/$PARTITION" ] && [ "$(readlink -f "/system/$PARTITION")" = "/$PARTITION" ]; then
|
||||||
if [ -d "/$1" ] && [ ! -L "/$1" ]; then
|
ui_print "- Handle partition /$PARTITION"
|
||||||
ui_print "- Handle partition /$1"
|
ln -sf "./system/$PARTITION" "$MODPATH/$PARTITION"
|
||||||
# we create a symlink if module want to access $MODPATH/system/$1
|
|
||||||
# but it doesn't always work(ie. write it in post-fs-data.sh would fail because it is readonly)
|
|
||||||
mv -f $MODPATH/system/$1 $MODPATH/$1 && ln -sf ../$1 $MODPATH/system/$1
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -423,23 +428,23 @@ install_module() {
|
||||||
[ -f $MODPATH/customize.sh ] && . $MODPATH/customize.sh
|
[ -f $MODPATH/customize.sh ] && . $MODPATH/customize.sh
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
handle_partition vendor true
|
||||||
|
handle_partition system_ext true
|
||||||
|
handle_partition product true
|
||||||
|
handle_partition odm false
|
||||||
|
|
||||||
# Handle replace folders
|
# Handle replace folders
|
||||||
for TARGET in $REPLACE; do
|
for TARGET in $REPLACE; do
|
||||||
ui_print "- Replace target: $TARGET"
|
ui_print "- Replace target: $TARGET"
|
||||||
mark_replace $MODPATH$TARGET
|
mark_replace "$MODPATH$TARGET"
|
||||||
done
|
done
|
||||||
|
|
||||||
# Handle remove files
|
# Handle remove files
|
||||||
for TARGET in $REMOVE; do
|
for TARGET in $REMOVE; do
|
||||||
ui_print "- Remove target: $TARGET"
|
ui_print "- Remove target: $TARGET"
|
||||||
mark_remove $MODPATH$TARGET
|
mark_remove "$MODPATH$TARGET"
|
||||||
done
|
done
|
||||||
|
|
||||||
handle_partition vendor
|
|
||||||
handle_partition system_ext
|
|
||||||
handle_partition product
|
|
||||||
handle_partition odm
|
|
||||||
|
|
||||||
if $BOOTMODE; then
|
if $BOOTMODE; then
|
||||||
mktouch $NVBASE/modules/$MODID/update
|
mktouch $NVBASE/modules/$MODID/update
|
||||||
rm -rf $NVBASE/modules/$MODID/remove 2>/dev/null
|
rm -rf $NVBASE/modules/$MODID/remove 2>/dev/null
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,6 @@ const KSU_IOCTL_GET_FEATURE: u32 = 0xc0004b0d; // _IOC(_IOC_READ|_IOC_WRITE, 'K'
|
||||||
const KSU_IOCTL_SET_FEATURE: u32 = 0x40004b0e; // _IOC(_IOC_WRITE, 'K', 14, 0)
|
const KSU_IOCTL_SET_FEATURE: u32 = 0x40004b0e; // _IOC(_IOC_WRITE, 'K', 14, 0)
|
||||||
const KSU_IOCTL_GET_WRAPPER_FD: u32 = 0x40004b0f; // _IOC(_IOC_WRITE, 'K', 15, 0)
|
const KSU_IOCTL_GET_WRAPPER_FD: u32 = 0x40004b0f; // _IOC(_IOC_WRITE, 'K', 15, 0)
|
||||||
const KSU_IOCTL_MANAGE_MARK: u32 = 0xc0004b10; // _IOC(_IOC_READ|_IOC_WRITE, 'K', 16, 0)
|
const KSU_IOCTL_MANAGE_MARK: u32 = 0xc0004b10; // _IOC(_IOC_READ|_IOC_WRITE, 'K', 16, 0)
|
||||||
const KSU_IOCTL_NUKE_EXT4_SYSFS: u32 = 0x40004b11; // _IOC(_IOC_WRITE, 'K', 17, 0)
|
|
||||||
const KSU_IOCTL_ADD_TRY_UMOUNT: u32 = 0x40004b12; // _IOC(_IOC_WRITE, 'K', 18, 0)
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
|
|
@ -75,31 +73,12 @@ struct ManageMarkCmd {
|
||||||
result: u32,
|
result: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Default)]
|
|
||||||
pub struct NukeExt4SysfsCmd {
|
|
||||||
pub arg: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Default)]
|
|
||||||
struct AddTryUmountCmd {
|
|
||||||
arg: u64, // char ptr, this is the mountpoint
|
|
||||||
flags: u32, // this is the flag we use for it
|
|
||||||
mode: u8, // denotes what to do with it 0:wipe_list 1:add_to_list 2:delete_entry
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark operation constants
|
// Mark operation constants
|
||||||
const KSU_MARK_GET: u32 = 1;
|
const KSU_MARK_GET: u32 = 1;
|
||||||
const KSU_MARK_MARK: u32 = 2;
|
const KSU_MARK_MARK: u32 = 2;
|
||||||
const KSU_MARK_UNMARK: u32 = 3;
|
const KSU_MARK_UNMARK: u32 = 3;
|
||||||
const KSU_MARK_REFRESH: u32 = 4;
|
const KSU_MARK_REFRESH: u32 = 4;
|
||||||
|
|
||||||
// Umount operation constants
|
|
||||||
const KSU_UMOUNT_WIPE: u8 = 0;
|
|
||||||
const KSU_UMOUNT_ADD: u8 = 1;
|
|
||||||
const KSU_UMOUNT_DEL: u8 = 2;
|
|
||||||
|
|
||||||
// Global driver fd cache
|
// Global driver fd cache
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
static DRIVER_FD: OnceLock<RawFd> = OnceLock::new();
|
static DRIVER_FD: OnceLock<RawFd> = OnceLock::new();
|
||||||
|
|
@ -315,47 +294,3 @@ pub fn mark_refresh() -> std::io::Result<()> {
|
||||||
ksuctl(KSU_IOCTL_MANAGE_MARK, &mut cmd as *mut _)?;
|
ksuctl(KSU_IOCTL_MANAGE_MARK, &mut cmd as *mut _)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nuke_ext4_sysfs(mnt: &str) -> anyhow::Result<()> {
|
|
||||||
let c_mnt = std::ffi::CString::new(mnt)?;
|
|
||||||
let mut ioctl_cmd = NukeExt4SysfsCmd {
|
|
||||||
arg: c_mnt.as_ptr() as u64,
|
|
||||||
};
|
|
||||||
ksuctl(KSU_IOCTL_NUKE_EXT4_SYSFS, &mut ioctl_cmd as *mut _)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wipe all entries from umount list
|
|
||||||
pub fn umount_list_wipe() -> std::io::Result<()> {
|
|
||||||
let mut cmd = AddTryUmountCmd {
|
|
||||||
arg: 0,
|
|
||||||
flags: 0,
|
|
||||||
mode: KSU_UMOUNT_WIPE,
|
|
||||||
};
|
|
||||||
ksuctl(KSU_IOCTL_ADD_TRY_UMOUNT, &mut cmd as *mut _)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add mount point to umount list
|
|
||||||
pub fn umount_list_add(path: &str, flags: u32) -> anyhow::Result<()> {
|
|
||||||
let c_path = std::ffi::CString::new(path)?;
|
|
||||||
let mut cmd = AddTryUmountCmd {
|
|
||||||
arg: c_path.as_ptr() as u64,
|
|
||||||
flags,
|
|
||||||
mode: KSU_UMOUNT_ADD,
|
|
||||||
};
|
|
||||||
ksuctl(KSU_IOCTL_ADD_TRY_UMOUNT, &mut cmd as *mut _)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete mount point from umount list
|
|
||||||
pub fn umount_list_del(path: &str) -> anyhow::Result<()> {
|
|
||||||
let c_path = std::ffi::CString::new(path)?;
|
|
||||||
let mut cmd = AddTryUmountCmd {
|
|
||||||
arg: c_path.as_ptr() as u64,
|
|
||||||
flags: 0,
|
|
||||||
mode: KSU_UMOUNT_DEL,
|
|
||||||
};
|
|
||||||
ksuctl(KSU_IOCTL_ADD_TRY_UMOUNT, &mut cmd as *mut _)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue