Làm cách nào để tạo SDK Android với các API ẩn và nội bộ có sẵn?


85

Tôi muốn xây dựng lại Android SDK (hay đúng hơn là chỉ android.jar) để bao gồm các API ẩn và nội bộ.

Tôi không thể tìm thấy bất kỳ tài liệu hoặc cuộc thảo luận nào về cách thực hiện điều này. Tôi đã thiết lập môi trường xây dựng Ubuntu CyanogenMod có thể tạo cm7.

Bây giờ, tôi đã đọc rằng make SDK sẽ tạo SDK nhưng tôi muốn tạo SDK bao gồm các phương thức và trường được đánh dấu là ẩn bằng cách sử dụng @hide. Điều này có khả thi không?

Điều tôi muốn làm là thực hiện các thay đổi đối với một ứng dụng sử dụng API ẩn và để xây dựng lại nó, tôi muốn sử dụng SDK đã sửa đổi.


2
@Hidden chỉ ẩn javadoc, tất cả những phương pháp này vẫn có sẵn
Blundell

12
@hide xóa chúng khỏi tệp lớp.
Thomas Hofmann


Tôi biết rằng tôi có thể sử dụng phản chiếu nhưng tôi muốn thay đổi một ứng dụng hiện có sử dụng API ẩn mà không cần nạp lại và tôi không muốn thay đổi tất cả mã thoát để sử dụng phản hồi.
Thomas Hofmann

4
Tôi nghĩ rằng bạn có thể xóa @Hiddennhãn của API bạn muốn truy cập, sau đó thực thi make update-apimake SDKtạo SDK của riêng bạn.
dreamtale

Câu trả lời:


68

Đây là những gì tôi luôn làm để sử dụng api ẩn.

  1. Tạo repo hoặc tải xuống các lọ từ https://sites.google.com/site/hippunosource/home/android/androidnohide-apiwo-shi-yongsuru-rifurekushonha-wei-shi-yong
  2. sao chép ra ngoài / target / common / obj / JAVA_LIBRARIES / framework_intermediates / Class.jar (tốt hơn nên đổi tên nó thành một cái gì đó giống như framework_all.jar)
  3. cấu hình đường dẫn xây dựng dự án của bạn -> thư viện -> thêm các lọ bên ngoài này. Trong Đặt hàng và Xuất, di chuyển nó lên và trước android.jar

Điều này cũng làm việc cho tôi! bây giờ tôi có thể tiếp tục với công việc thực tế, nhờ cho điều này
CurlyPaul

IMHO, Đây là cách dễ nhất và nhanh nhất.
Patrick Cho

Không có cách nào để đánh dấu đây là câu trả lời thích hợp?
Chris Browet

2
phương pháp này hoạt động với tất cả các thiết bị hay nó chỉ hoạt động với thiết bị được nhắm mục tiêu?
Hải Phòng

Để xác định thứ tự thư viện trong android studio, bạn cần thay đổi .imltệp của mô-đun và đưa lib mong muốn của bạn <orderEntry>trước Android SDK . Thật không may, kỹ thuật này không bền vì tệp sẽ bị ghi đè sau khi nhấn nút đồng bộ hóa gradle.
waqaslam

44

Tôi đã thực hiện một số điều tra về điều này, và kết luận của tôi đơn giản là: Điều này không thể được thực hiện nếu không có một chút công việc. Đọc phần còn lại của câu trả lời này để biết chi tiết về những gì tôi đã tìm thấy.


android.jarthực sự bao gồm "api công cộng" của framework.jarcore.jarđược tìm thấy trong system/frameworks/thiết bị. android.jarlà một loại mà tôi sẽ gọi là tiêu đề thư viện Java, tất cả triển khai trong mã byte thực tế chỉ là a throw new RuntimeException("stub");, điều này cho phép bạn xây dựng dựa trên android.jar(ví dụ: trong Eclipse), nhưng việc thực thi phải được thực hiện trên thiết bị hoặc trình giả lập.

API công khai của Android SDK được xác định bởi các lớp / phương thức / trường không có tiền tố là @{hide}chú thích javadoc. Tức là mọi thứ không được chú thích đều được đưa vào SDK.

android.jarđược xây dựng từ các nguồn nằm trong out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediatesđó chính nó được tạo ra bởi công cụ DroidDoc nằm trong đó build/tools/droiddoc.

DroidDoc là công cụ (có thể được điều chỉnh từ javadoc hoặc sử dụng javadoc) tạo tài liệu Android SDK thực tế. Như một tác dụng phụ, và có lẽ vì nó đã phân tích cú pháp tất cả javadoc, nó cũng tạo ra các bản gốc android sau đó được biên dịch thành các bản android.jarphân phối trong SDK.

Vì vậy, bạn có thể bao gồm những thứ bị ẩn, nếu chỉ muốn bao gồm các phần cụ thể, chỉ cần xóa @hidechú thích và xây dựng lại SDK.

Tuy nhiên, nếu bạn muốn bao gồm tất cả các phần ẩn, mọi thứ sẽ phức tạp hơn rất nhiều. Bạn có thể sửa đổi DroidDoc (có nguồn liên quan build/tools/droiddoc/src/Stubs.java) để không có gì bị phát hiện là ẩn. Điều này là khá nhỏ và tôi đã thử điều này, tuy nhiên các sơ khai sau đó được tạo ra không biên dịch chút nào.

Kết luận của tôi bây giờ là điều này đơn giản là không khả thi. Các sơ khai được tạo ra nếu bạn xóa phần DroidDoc phát hiện các chú thích ẩn, đơn giản là không thể biên dịch được và sẽ yêu cầu khá nhiều công việc để biên dịch chính xác.

Vì vậy, câu trả lời của tôi cho câu hỏi của bạn là: Không, điều này không thể được thực hiện, nếu không làm rất nhiều việc. Lấy làm tiếc.


Một lưu ý phụ về mkstubscông cụ. mkstubsđược sử dụng khi bạn tạo phần bổ trợ SDK , tức là phần bổ trợ bạn có thể tìm thấy trong trình quản lý SDK Android từ các nhà cung cấp, ví dụ: Samsung cung cấp cho bạn một API bổ sung cho nội dung cụ thể cho điện thoại Samsung. mkstubsgiống như quá trình tạo sơ khai DroidDoc, tuy nhiên nó không sử dụng @hidechú thích, nó sử dụng một .defstệp mô tả gói / lớp / trường nào cần bao gồm hoặc loại trừ khỏi trình bổ trợ SDK của bạn.

Tuy nhiên, tất cả điều này đều không liên quan đến câu hỏi, vì bản dựng Android SDK không sử dụng mkstubscông cụ. (Không may.)


Tôi cũng có một cái nhìn. Ngoài Droiddoc còn có một công cụ gọi là mkstubs trong / development / tools / mkstubs. Nó được gọi trong quá trình xây dựng và theo như tôi thấy, nó sẽ sửa đổi các tệp lớp khai thác nội dung ra khỏi chúng. Trong build / core / task / sdk-addon.mk có đoạn mã sau: định nghĩa gốc-addon-jar $ (gọi tệp tin gốc-addon-jar-, $ (1)): $ (1) | mkstubs $ (thông tin Stubbing addon jar bằng $ (PRODUCT_SDK_ADDON_STUB_DEFS)) $ (hide) java -jar $ (call-module-install-files, mkstubs) $ (if $ (hide) ,, - v) \ "$$ <" "$$ @" @ $ (PRODUCT_SDK_ADDON_STUB_DEFS) endef
Thomas Hofmann

Thật không may, tôi không thích thứ này nhưng với tôi, có vẻ như tất cả đều phụ thuộc vào một biến được gọi là ẩn. Tôi không tìm thấy nó đã được thiết lập mặc dù. Nếu bạn tìm kiếm $ (hide) trong các tệp xây dựng khác, bạn sẽ thấy rằng rất nhiều phụ thuộc vào giá trị này. Nó dường như cũng ảnh hưởng đến cách C libs được xây dựng.
Thomas Hofmann

@ThomasHofmann: Ban đầu tôi cũng thấy bối rối với công cụ mkstubs. Những gì tôi học được là mkstubs chỉ được sử dụng khi bạn đang xây dựng một addon sdk (nhà cung cấp), không phải khi bạn chỉ xây dựng sdk bình thường. Tuy nhiên, mkstubs hoạt động giống như DroidDoc, ngoại trừ nó không sử dụng @hidechú thích, nó "chỉ" sử dụng một .defstệp mô tả gói / lớp / trường nào sẽ được đưa vào API của addon.
Bjarke Freund-Hansen

2
@ThomasHofmann: Về việc $(hide), bạn đang tự nhầm lẫn. $(hide)chỉ đơn giản là một tiền tố trong makefiles để ẩn dòng lệnh thực sự của chương trình đang được thực thi, không có gì hơn, và nó được sử dụng ở khắp mọi nơi ở mọi nơi. Nó không liên quan gì đến Android SDK hoặc @hidechú thích trong mã nguồn.
Bjarke Freund-Hansen

Làm cách nào để tôi có thể kiểm tra đường dẫn trên đây / target / common / obj / JAVA_LIBRARIES / framework_intermediates / class.jar
Bunny

31

Chúng tôi có thể tạo lại các tệp * .jar từ nền tảng Android.

Đầu tiên, kết nối ADB với thiết bị của bạn. Sau đó chạy:

adb pull /system/framework/core.jar .
adb pull /system/framework/framework.jar .

Hộp core.jarchứa các thư viện Java tiêu chuẩn ( java.*) và framework.jarchứa các thư viện Android ( android.*). Điều này vẫn chưa thể sử dụng được, vì các tệp thực tế ở định dạng DEX, không phải định dạng JAR.

Chúng tôi có thể chuyển đổi * .jars định dạng DEX này thành các JAR thực sự bằng cách sử dụng các công cụ như dex2jar :

dex2jar core.jar
dex2jar framework.jar

Sau đó kéo vào các lọ này bằng cách sử dụng "Thêm JAR bên ngoài ..." (giả sử bạn đang sử dụng Eclipse ADT)

  • nhấp chuột phải vào Project → Properties → Java Build Path → Libraries → Add External JARs... → (Chọn core-dex2jar.jarframework-dex2jar.jartừ trên).

Điều này sẽ cho phép bạn sử dụng nội bộ và một số API Java 7. (Theo như tôi thấy, APK đã tạo không chứa bất kỳ mã thực tế nào từ JAR.)


Cảm ơn bạn rất nhiều, tôi đã thử nhiều cách chỉ có phương pháp của bạn phù hợp với tôi.
SalutonMondo

7
Điều quan trọng cần lưu ý là phương pháp này vẫn hoạt động với ICS và các hệ thống mới hơn, nhưng yêu cầu thêm một số thao tác. Các tập tin có liên quan /system/framework/core.odex, /system/framework/framework.odexvà có lẽ nhiều. Chúng có thể được deodexed ( java -jar baksmali-2.0.3.jar -d system.framework -x system.framework/core.odex -o core) và reodexed ( java -jar smali-2.0.3.jar -x -o core.dex core), và chỉ sau đó dex2jar core.dexnó thực hiện công việc của nó.
Alex Cohn

không thể tìm thấy core.jar trong marshmallow
mehmet6parmak

bạn có ý tưởng gì cho core & framework jar trong android p không?
Prabhakaran

Làm cách nào để tôi có thể kiểm tra đường dẫn trên đây / target / common / obj / JAVA_LIBRARIES / framework_intermediates / class.jar
Bunny

17

Đối với Lollipop, luồng hơi khác một chút:

  1. Nhận /system/framework/arm/boot.oat từ thiết bị kẹo mút

  2. Sử dụng 'java -jar yến mạch2dex.jar khởi động boot.oat'

  3. Bạn sẽ nhận được hai thư mục: dex và odex. Đi tới dex và tạo 'java -jar dex2jar.jar framework.dex'
  4. Đổi tên framework.jar kết quả thành .zip, giải nén và tìm các lớp bạn cần
  5. Truy cập [sdk_path] / platform / [target_platform] và giải nén android.jar (đầu tiên đổi tên nó thành zip).
  6. Sao chép các tệp từ khung được giải nén sang android.jar được giải nén. Sau đó nén thành zip và đổi tên thành .jar :)

ps: có thể bạn cần lặp lại các bước 4-6 cho 'framework_classes2.dex'


Đối với bước 3, tôi không thể tìm thấy dex2jar.jar tại liên kết đó .... Tôi đã thử rất nhiều thứ và tôi không thể tìm ra. Có liên kết đến điều đó ở đâu đó không? Nó có nằm trong SDK Android của tôi không? Tôi không thể tìm thấy nó.
Dwebtron

Yeah, nó đi đến một dự án github, và có lẽ đó là tôi, nhưng tôi không thể tìm thấy bất kỳ tập tin kết thúc bằng ".jar" đó ...
Dwebtron

1
xem phần 'phát hành'
lệch

2
Vì vậy, có vẻ như các phiên bản gần đây của dex2jar đã thay đổi định dạng tải xuống. Chỉ cần giải nén bản tải xuống và thay vì 'java -jar ...', chỉ cần chạy tập lệnh 'd2j-dex2jar.sh' hoặc 'd2j-dex2jar.bat', tùy thuộc vào nền tảng của bạn, trực tiếp trên tệp framework.dex
CalumMcCall

1
tôi đã sao chép cả hai tệp jar vào android.jar, bây giờ android studio cho tôi biết Lỗi: Thực thi không thành công cho tác vụ ': app: processDebugResources'. > com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Xử lý 'lệnh' D: \ Program \ Android \ android-sdk \ build-tools \ 19.1.0 \ aapt.exe ' 'kết thúc với giá trị khác không exit 1
wutzebaer

16

Bạn có thể tải xuống bản sửa đổi android.jarđể được sử dụng làm API ẩn từ kho lưu trữ này . Làm theo hướng dẫn ở đó.


4
Câu trả lời này nên được ủng hộ nhiều hơn vì nó là giải pháp đơn giản nhất. Mọi người, nếu bạn đang tìm kiếm giải pháp - hãy biết rằng những người khác đã làm điều đó và đặt giải pháp cho repo github đó. Sử dụng nó và tận hưởng! ))
Mixaz

1
Trong khi sử dụng android.jar cho API 28, tôi gặp lỗi với robolectric. Đã nêu ra vấn đề github.com/anggrayudi/android-hipris-api/issues/62 và cảm ơn bạn vì jar của bạn @Anggrayudi
Prabhakaran

tôi cũng đã làm tương tự cho android 10, nó không hoạt động với tôi thay vì tôi đã tải xuống từ người đóng góp khác github.com/aeab13/android-jar-with-hidden-api . tôi đã có thể truy cập lớp hdmi-cec và xây dựng nó nhưng các mã vẫn còn gây nhầm lẫn .code link android.googlesource.com/platform/frameworks/base/+/4e90fcd/…
babbin tandukar

15

DroidCon 2011

Dưới đây Erik Hellman từ Sony Ericson giải thích cách truy cập API ẩn của Android:

http://vimeo.com/30180393 (Liên kết Hmm dường như không hoạt động).

Goto DroidCon trang web Ngày 2 cuộn xuống để dùng ẩn API 10:15 và bạn có thể xem nó ở đó.

Liên kết đang chết!

Tôi đã tìm thấy cái này: http://skillsmatter.com/podcast/os-mobile-server/hiised-api dunno nó sẽ hoạt động trong bao lâu

Các API chính thức trong Android SDK thường đủ cho hầu hết các ứng dụng thông thường. Tuy nhiên, đôi khi có những trường hợp nhà phát triển cần quyền truy cập vào các dịch vụ hệ thống nội bộ, các API và tài nguyên không được xuất bản trong các API chính thức. May mắn thay, các API này vẫn có sẵn thông qua một số thủ thuật thông minh và thường có thể hữu ích khi phát triển giải pháp mới và sáng tạo trên Android. Trong phần này, bạn sẽ học cách truy cập và sử dụng các API ẩn và được bảo vệ này, các hạn chế của việc sử dụng chúng và một số mẹo nhỏ về cách sử dụng chúng một cách an toàn và có kiểm soát trên nhiều thiết bị của nhà cung cấp và phiên bản Android. Khán giả sẽ thấy một số bản trình diễn nâng cao mà bạn thường không thể làm với Android. Mong đợi một phiên khá nâng cao với nhiều thông tin chi tiết về nội bộ của nền tảng Android.


1
Thật thú vị nhưng tiếc là nó không trả lời câu hỏi của tôi. Tôi đang yêu cầu một cách để thực sự xây dựng lại SDK có bao gồm những thứ ẩn.
Thomas Hofmann

Có vẻ như tôi đã tìm ra một cách khác để đạt được điều mình muốn. Tôi sẽ mô tả nó vào ngày mai.
Thomas Hofmann

Tôi phát hiện ra rằng nếu bạn muốn biên dịch nguồn của một dự án sử dụng API ẩn trong ADT, bạn có thể làm như sau: 1) Tạo một dự án Android cho nguồn. 2) Xóa vùng chứa classpath của Android khỏi đường dẫn xây dựng 3) Xác định thư viện người dùng (cũng chọn hộp kiểm thư viện hệ thống) bao gồm các tệp JAR từ bản dựng ROM ASOP, ví dụ: cm7). Bạn sử dụng tệp JAR nào tùy thuộc vào những gì bạn cần tham khảo. framework-ngay lập tức có thể sẽ là một phần của nó.
Thomas Hofmann,

4) Khi dự án hiện đã được xây dựng, các lớp thư viện người dùng sẽ không được đưa vào APK đang được tạo. API ẩn có thể nhìn thấy và mọi thứ sẽ biên dịch tốt.
Thomas Hofmann

@Blundell: Bạn có thể vui lòng cập nhật các liên kết được không .. họ đã chết!
zombie

12

Hãy thử nhìn vào điều này :

Mục tiêu cuối cùng của các bài viết này là cung cấp cho các nhà phát triển sức mạnh của các API nội bộ và ẩn mà không sử dụng phản chiếu. Nếu bạn hoàn thành tất cả các bước được mô tả trong một số phần tiếp theo, bạn sẽ có thể sử dụng các API nội bộẩn như thể chúng là các API mở công khai. Sẽ không cần phải suy tư.

Nhưng nếu bạn đang sử dụng các API không công khai này thì bạn nên biết rằng ứng dụng của bạn có rủi ro lớn. Về cơ bản, không có gì đảm bảo rằng các API sẽ không bị hỏng với bản cập nhật tiếp theo cho hệ điều hành Android. Thậm chí không có gì đảm bảo về hành vi nhất quán trên các thiết bị từ các nhà cung cấp khác nhau. Bạn hoàn toàn là của riêng bạn.

Có ba tình huống bạn có thể muốn làm theo:

  1. Bật cả API nội bộẩn (trường hợp A)
  2. Chỉ bật API ẩn (kịch bản B)
  3. Chỉ bật API nội bộ (kịch bản C)

Kịch bản A là tổng của B và C. Tình huống B là kịch bản dễ nhất (không yêu cầu sửa đổi plugin eclipse ADT).

Tình huống A : đọc phần 1 , 2 , 3 , 4 , 5

Tình huống B : đọc phần 1 , 2 , 3 , 5

Tình huống C : đọc phần 1 , 2 , 3 , 4 , 5


3
Tôi đã đọc mục blog đó. Nó đề cập đến điều này: "1) Android là một dự án mã nguồn mở. Chúng tôi có thể tải xuống mã nguồn và tùy chỉnh hệ thống xây dựng để nó không loại trừ các lớp nội bộ và ẩn khỏi android.jar. Đây là một cách khó." Thật không may, nó không đi vào chi tiết.
Thomas Hofmann

3
Đi đến phần cuối của bài đăng trên blog ( devmaze.wordpress.com/2011/01/19/… ), có một liên kết ( github.com/inazaruk/android-sdk/tree/master/platforms ) đến Android được cài sẵn API với tất cả các API ẩn và nội bộ.
Bob

1
Các liên kết đã cho không thể truy cập được, yêu cầu sự cho phép của tác giả
SHAHS

Làm thế nào tôi có thể kiểm tra đường dẫn trên này ra / target / common / obj / JAVA_LIBRARIES / framework_intermediates / class.jar
Bunny

1

Tôi đã từng viết một số tập lệnh Groovy để trích xuất các tệp java từ một kho kiểm tra từ http://source.android.com/ và sau đó biên dịch chúng mà không cần chuỗi công cụ đầy đủ để biên dịch tất cả các nguồn android, bao gồm các bước cần thiết khác ( đóng gói, tạo tài nguyên, v.v.).

Chúng có thể được tìm thấy ở đây:

https://github.com/thoutbeckers/CollectAndroid

Nhưng chắc chắn điều này sẽ cần cập nhật cho bất kỳ thứ gì sau Gingerbread, chủ yếu bằng cách đặt đúng thư mục trong "rootdirs" trong tệp cấu hình (CollectConfig.groovy).

Vào thời điểm đó, tôi thường xuyên sử dụng nó để phát triển với tất cả các nguồn và API ẩn (cũng có vấn đề vào thời điểm đó) có sẵn.

Như đã đề cập ở những nơi khác, com / android / internal / ** sẽ vẫn bị ẩn trong các phiên bản gần đây của ADT do quy tắc truy cập được quảng cáo.


Làm cách nào để tôi có thể kiểm tra đường dẫn ở trên này / target / common / obj / JAVA_LIBRARIES / framework_intermediates / class.jar
Bunny

Tôi đã lưu trữ repo đó kể từ khi nó được cập nhật lần cuối cho Gingerbread . Rất tiếc, bạn sẽ phải tìm một cách tiếp cận khác.
thoutbeckers

1

Câu trả lời của Long phù hợp với tôi, nhưng tôi vẫn thiếu một số lớp tôi cần, cụ thể là android.provider.Telephony. Tôi đã có thể thêm nó như thế này:

  1. Giải nén tệp framework.jar

    mkdir /tmp/framework
    cp framework.jar /tmp
    cd /tmp/framework
    jar xvf ../framework.jar
    mv android classes
    
  2. Tạo repo Android, sẽ tạo thư mục out / target / common / obj / JAVA_LIBRARIES

  3. Tìm các lớp bị thiếu ở đâu

    $ cd /path/to/out/target/common/obj/JAVA_LIBRARIES
    $ find . | grep "/Telephony.class"
    ./telephony-common_intermediates/classes/android/provider/Telephony.class
    ./android_stubs_current_intermediates/classes/android/provider/Telephony.class
    
  4. Thêm các lớp mới và xây dựng lại tệp JAR khung

    cd /tmp/framework
    cp -r /path/to/out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes .
    cp -r /path/to/out/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates/classes .
    cd classes
    jar cvf ../framework.jar .
    

Hoặc bạn có thể lười biếng và gộp tất cả các lớp vào một tệp jar khổng lồ:

cd /tmp/framework
cp -r /path/to/out/target/common/obj/JAVA_LIBRARIES/*/classes .
cd classes
jar cvf ../framework.jar .

out / target / common / obj / JAVA_LIBRARIES làm thế nào để tìm thấy đường dẫn này?
Bunny

@Bunny Tôi đã cập nhật câu trả lời của mình với nhiều chi tiết hơn. Nhưng có vẻ như câu trả lời ở đây có thể dễ dàng hơn: stackoverflow.com/a/32626155/399105
bmaupin

cảm ơn vì phản hồi của bạn @bmaupin .. bạn có thể vui lòng giúp tôi cách tìm đường dẫn này ra / target / common / obj / JAVA_LIBRARIES để tải framework jar ... tôi thực sự gặp khó khăn..Tôi không thể tìm thấy đường dẫn này hoặc thiếu cái gì đó ... xin vui lòng hướng dẫn
Bunny

0

Tôi không thể nhận xét nhưng về cơ bản đây là một nhận xét cho câu trả lời tuyệt vời của @ KennyTM ( https://stackoverflow.com/a/13550030/2923406 ):

Nếu bạn gặp lỗi sau trong Eclipse:

The type com.android.internal.util.Predicate cannot be resolved. It is indirectly referenced from required .class   files

(nghĩa là android.internal. * không khả dụng)

Sau đó, một giải pháp khả thi là áp dụng cùng một phương pháp cho /system/framework/framework2.jar. Sử dụng Trình giả lập Android cho SDK19, tôi có thêm lọ này. Trên HTC One của tôi thậm chí còn có một framework3.jar.


Làm cách nào để tôi có thể kiểm tra đường dẫn ở trên này / target / common / obj / JAVA_LIBRARIES / framework_intermediates / class.jar
Bunny
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.