CẢNH BÁO: OpenSL ES không còn được dùng nữa. Nhà phát triển nên sử dụng thư viện Oboe nguồn mở có trên GitHub. Oboe là một trình bao bọc C++, cung cấp một API gần giống với AAudio. Oboe gọi AAudio khi có API này và quay lại sử dụng OpenSL ES nếu không có AAudio.
Các ghi chú trong phần này bổ sung cho quy cách OpenSL ES 1.0.1.
Khởi tạo giao diện và đối tượng
Mô hình lập trình OpenSL ES có hai khía cạnh mà có thể nhiều nhà phát triển mới chưa quen thuộc, đó là trình tự khởi chạy và sự khác biệt giữa đối tượng và giao diện.
Nói ngắn gọn, đối tượng OpenSL ES cũng tương tự như khái niệm đối tượng trong các ngôn ngữ lập trình như Java và C++, ngoại trừ việc đối tượng OpenSL ES chỉ xuất hiện qua các giao diện được liên kết.
Trong đó có giao diện ban đầu dành cho mọi đối tượng, có tên là SLObjectItf
.
Đối tượng không có handle dành cho chính nó mà chỉ có một handle dành cho giao diện SLObjectItf
của đối tượng đó.
Trước hết, một đối tượng OpenSL ES được tạo, trả về một SLObjectItf
, sau đó được hiện thực hoá. Cách thức này cũng tương tự như mô hình lập trình phổ biến để xây dựng đối tượng đầu tiên (sẽ không bao giờ gặp lỗi ngoài trường hợp thiếu bộ nhớ hoặc thông số không hợp lệ), sau đó hoàn tất bước khởi tạo (có thể không thành công do thiếu tài nguyên). Bước hiện thực hoá này là cơ sở hợp lý để phân bổ thêm tài nguyên (nếu cần) cho việc triển khai.
Là một phần của API để tạo một đối tượng, ứng dụng chỉ định một mảng giao diện mong muốn mà ứng dụng dự định lấy sau đó. Xin lưu ý rằng mảng này không tự động lấy giao diện mà chỉ cho biết ý định lấy giao diện trong tương lai. Các giao diện được phân biệt thành ngầm ẩn (implicit) hoặc rõ ràng (explicit). Giao diện rõ ràng phải được liệt kê trong mảng tạo đối tượng nếu sau đó ứng dụng sẽ lấy giao diện đó. Giao diện ngầm ẩn không cần phải được liệt kê trong mảng tạo đối tượng, nhưng việc liệt kê đó sẽ không có tác hại gì. OpenSL ES có thêm một loại giao diện được gọi là giao diện động (dynamic). Giao diện động không cần phải được chỉ định trong mảng tạo đối tượng và có thể được thêm vào sau khi tạo đối tượng. Quá trình triển khai của Android cung cấp một tính năng tiện lợi để tránh sự phức tạp này. Tính năng đó được mô tả trong nội dung Giao diện động khi tạo đối tượng.
Sau khi tạo và hiện thực hoá đối tượng, ứng dụng sẽ lấy các giao diện dành cho mỗi tính năng mà đối tượng cần có, sử dụng GetInterface
trên SLObjectItf
ban đầu.
Cuối cùng, bạn có thể sử dụng đối tượng này thông qua các giao diện của đối tượng. Tuy nhiên, cần lưu ý rằng có một số đối tượng sẽ yêu cầu bạn phải thiết lập thêm. Cụ thể, trình phát âm thanh với nguồn dữ liệu URI cần chuẩn bị thêm một chút để phát hiện lỗi kết nối. Xem nội dung Tìm nạp trước trình phát âm thanh để biết thông tin chi tiết.
Sau khi sử dụng xong đối tượng, bạn nên thể hiện rõ việc huỷ đối tượng đó; xem nội dung Huỷ bỏ dưới đây.
Tìm nạp trước trình phát âm thanh
Đối với trình phát âm thanh có nguồn dữ liệu URI, Object::Realize
sẽ phân bổ tài nguyên nhưng không kết nối với nguồn dữ liệu (chuẩn bị) hoặc bắt đầu tìm nạp trước dữ liệu. Điều này xảy ra khi trạng thái trình phát được đặt là SL_PLAYSTATE_PAUSED
hoặc SL_PLAYSTATE_PLAYING
.
Một số thông tin có thể vẫn chưa được xác định cho đến khi tương đối muộn trong trình tự này. Cụ thể, ban đầu, Player::GetDuration
trả về SL_TIME_UNKNOWN
và MuteSolo::GetChannelCount
trả về thành công số lượng kênh bằng 0 hoặc kết quả lỗi SL_RESULT_PRECONDITIONS_VIOLATED
. Các API này sẽ trả về giá trị thích hợp khi đã xác định đủ thông tin.
Có một số thuộc tính khác ban đầu chưa được xác định như tốc độ lấy mẫu và kiểu nội dung phương tiện thực tế dựa trên việc kiểm tra tiêu đề của nội dung (trái ngược với kiểu MIME do ứng dụng chỉ định và kiểu vùng chứa). Các thuộc tính này sau đó cũng được xác định trong quá trình chuẩn bị/tìm nạp trước, nhưng không có API nào để truy xuất.
Giao diện trạng thái tìm nạp trước sẽ hữu ích cho việc phát hiện tất cả thông tin hiện có hoặc ứng dụng của bạn có thể thăm dò theo định kỳ. Xin lưu ý rằng một số thông tin (chẳng hạn như thời lượng phát trực tuyến MP3) có thể không bao giờ được xác định.
Giao diện trạng thái tìm nạp trước cũng hữu ích cho việc phát hiện lỗi. Đăng ký một lệnh gọi lại và cho phép ít nhất là các sự kiện SL_PREFETCHEVENT_FILLLEVELCHANGE
và SL_PREFETCHEVENT_STATUSCHANGE
. Nếu cả hai sự kiện này được phân phối đồng thời và PrefetchStatus::GetFillLevel
báo cáo mức 0 và PrefetchStatus::GetPrefetchStatus
báo cáo SL_PREFETCHSTATUS_UNDERFLOW
, thì tức là có một lỗi không khôi phục được trong nguồn dữ liệu. Lỗi này bao gồm việc không thể kết nối với nguồn dữ liệu do tên tệp cục bộ không tồn tại hoặc URI mạng không hợp lệ.
Theo dự kiến, phiên bản tiếp theo của OpenSL ES sẽ hỗ trợ rõ ràng hơn về việc xử lý lỗi trong nguồn dữ liệu. Tuy nhiên, để đảm bảo khả năng tương thích với tệp nhị phân sau này, chúng tôi dự định tiếp tục hỗ trợ phương thức hiện tại để báo cáo lỗi không khôi phục được.
Tóm lại, trình tự mã được đề xuất như sau:
Engine::CreateAudioPlayer
Object:Realize
Object::GetInterface
choSL_IID_PREFETCHSTATUS
PrefetchStatus::SetCallbackEventsMask
PrefetchStatus::SetFillUpdatePeriod
PrefetchStatus::RegisterCallback
Object::GetInterface
choSL_IID_PLAY
Play::SetPlayState
đếnSL_PLAYSTATE_PAUSED
hoặcSL_PLAYSTATE_PLAYING
Lưu ý: Quá trình chuẩn bị và tìm nạp trước xảy ra ở đây; trong thời gian này, lệnh gọi lại của bạn được gọi bằng các bản cập nhật trạng thái định kỳ.
Huỷ bỏ
Hãy nhớ huỷ bỏ tất cả đối tượng khi ứng dụng của bạn thoát. Bạn nên huỷ bỏ các đối tượng theo thứ tự ngược với thứ tự khi tạo ra, lý do là không an toàn khi huỷ bỏ một đối tượng có một hoặc nhiều đối tượng phụ thuộc. Ví dụ: hãy huỷ bỏ theo thứ tự này: trình phát âm thanh (audio player) và máy ghi âm (recorder), bộ trộn đầu ra (output mix) và cuối cùng là công cụ (engine).
OpenSL ES không hỗ trợ tính năng thu thập rác tự động hay đếm số lượt tham chiếu của các giao diện. Sau khi bạn gọi Object::Destroy
, tất cả giao diện hiện có bắt nguồn từ đối tượng được liên kết sẽ trở thành không xác định.
Quá trình triển khai của Android OpenSL ES không phát hiện được việc sử dụng sai các giao diện đó. Việc tiếp tục sử dụng các giao diện đó sau khi đối tượng bị huỷ có thể khiến ứng dụng của bạn gặp sự cố hoặc hoạt động theo cách không lường trước được.
Bạn nên thiết lập rõ ràng cả giao diện đối tượng chính lẫn tất cả giao diện được liên kết thành NULL
trong trình tự huỷ bỏ đối tượng. Điều này giúp tránh việc vô tình sử dụng nhầm handle của giao diện cũ.
Di chuyển âm thanh nổi
Khi bạn dùng Volume::EnableStereoPosition
để bật tính năng di chuyển âm thanh nổi của nguồn đơn âm (mono), tổng mức công suất âm thanh sẽ giảm đi 3 dB. Việc này là để cho phép tổng mức công suất âm thanh không đổi khi nguồn được di chuyển từ kênh này sang kênh khác. Do đó, chỉ bật chế độ định vị âm thanh nổi (stereo) khi bạn thực sự cần. Để biết thêm thông tin, hãy xem bài viết trên Wikipedia về di chuyển âm thanh.
Lệnh gọi lại và luồng
Trình xử lý gọi lại thường được gọi một cách đồng bộ khi quá trình triển khai phát hiện một sự kiện. Điểm này không đồng bộ với ứng dụng, vì vậy bạn nên sử dụng cơ chế đồng bộ hoá không chặn để kiểm soát quyền truy cập vào biến bất kỳ được chia sẻ giữa ứng dụng và trình xử lý gọi lại. Trong mã mẫu, chẳng hạn như đối với hàng đợi bộ đệm, chúng tôi đã bỏ qua việc đồng bộ hoá này hoặc sử dụng cơ chế đồng bộ hoá chặn để đơn giản hoá. Tuy nhiên, quá trình đồng bộ hoá không chặn phù hợp là rất quan trọng đối với mọi mã phát hành chính thức.
Các trình xử lý gọi lại được gọi qua các luồng nội bộ không phải của ứng dụng không được đính kèm vào môi trường thời gian chạy Android, vì vậy, các trình xử lý này không đủ điều kiện để sử dụng JNI. Vì các luồng nội bộ này quan trọng đối với tính toàn vẹn của quá trình triển khai OpenSL ES, nên trình xử lý gọi lại cũng không chặn hoặc thực hiện quá nhiều việc.
Nếu trình xử lý gọi lại của bạn cần sử dụng JNI hoặc thực hiện công việc không tương ứng với lệnh gọi lại, thì trình xử lý sẽ đăng một sự kiện vào một luồng khác để xử lý. Có thể kể đến một số ví dụ về khối lượng công việc gọi lại được chấp nhận như hoạt động kết xuất và xếp hàng vùng đệm đầu ra tiếp theo (đối với một AudioPlayer), xử lý vùng đệm đầu vào vừa được điền và xếp hàng vùng đệm trống tiếp theo (đối với một AudioRecorder), hoặc các API đơn giản, chẳng hạn như hầu hết các API nhóm Get. Hãy xem phần Hiệu suất dưới đây về khối lượng công việc.
Xin lưu ý rằng quá trình nghịch đảo là an toàn: một luồng ứng dụng Android đã nhập JNI được phép gọi trực tiếp cho các API OpenSL ES, kể cả các API chặn. Tuy nhiên, bạn không nên chặn các lệnh gọi qua luồng chính vì việc này có thể dẫn đến lỗi Ứng dụng không phản hồi (Application Not Responding – ANR).
Quá trình xác định liên quan đến luồng gọi trình xử lý gọi lại phần lớn phụ thuộc vào quá trình triển khai. Lý do của tính linh hoạt này là để cho phép tối ưu hoá trong tương lai, đặc biệt là trên các thiết bị có bộ xử lý nhiều nhân.
Luồng nơi trình xử lý gọi lại chạy không được đảm bảo sẽ có cùng danh tính trong nhiều lệnh gọi. Do đó, đừng dựa vào pthread_t
mà pthread_self()
trả về hoặc pid_t
mà gettid()
trả về để đảm bảo nhất quán với các lệnh gọi. Vì cùng lý do này, đừng sử dụng các API lưu trữ cục bộ luồng (thread local storage – TLS) như pthread_setspecific()
và pthread_getspecific()
trong một lệnh gọi lại.
Quá trình triển khai đảm bảo rằng các lệnh gọi lại đồng thời thuộc cùng một loại và cho cùng một đối tượng sẽ không xảy ra. Tuy nhiên, bạn có thể dùng các lệnh gọi lại đồng thời thuộc các loại khác nhau cho cùng một đối tượng trên các luồng khác nhau.
Hiệu suất
Vì OpenSL ES là một API C gốc, nên các luồng ứng dụng không thuộc môi trường thời gian chạy gọi OpenSL ES sẽ không có mức hao tổn liên quan đến môi trường thời gian chạy, chẳng hạn như các khoảng tạm dừng để thu thập rác. Với một ngoại lệ được mô tả dưới đây, việc sử dụng OpenSL ES không đem lại thêm lợi ích hiệu suất nào khác. Cụ thể, việc sử dụng OpenSL ES không đảm bảo việc dùng được các tính năng nâng cao mà nền tảng thường cung cấp, chẳng hạn như độ trễ âm thanh thấp hơn và mức độ ưu tiên cao hơn cho việc lập lịch biểu. Mặt khác, khi nền tảng Android và các hoạt động triển khai trên các thiết bị cụ thể tiếp tục phát triển, ứng dụng OpenSL ES có thể được hưởng lợi nhờ mọi cải tiến hiệu suất hệ thống trong tương lai.
Một trong những cải tiến nói trên là tính năng hỗ trợ giảm thiểu độ trễ đầu ra âm thanh.
Cơ sở để giảm thiểu độ trễ đầu ra lần đầu tiên được đưa vào Android 4.1 (API cấp 16) và sau đó tiến trình này vẫn tiếp tục trong Android 4.2 (API cấp 17). Những cải tiến này có sẵn thông qua OpenSL ES dành cho các hoạt động triển khai thiết bị có xác nhận tính năng android.hardware.audio.low_latency
.
Nếu thiết bị không xác nhận tính năng này nhưng có hỗ trợ Android 2.3 (API cấp 9) trở lên, bạn vẫn có thể sử dụng các API OpenSL ES nhưng độ trễ đầu ra có thể cao hơn.
Đường dẫn độ trễ đầu ra thấp hơn chỉ được dùng nếu ứng dụng yêu cầu dung lượng bộ nhớ đệm và tốc độ lấy mẫu tương thích với cấu hình đầu ra gốc của thiết bị. Các thông số này tuỳ thuộc vào từng thiết bị và sẽ được thu thập theo mô tả dưới đây.
Kể từ Android 4.2 (API cấp 17), ứng dụng có thể truy vấn tốc độ lấy mẫu đầu ra gốc hoặc tối ưu của nền tảng và dung lượng bộ nhớ đệm dành cho luồng đầu ra chính của thiết bị. Khi kết hợp với việc thử nghiệm tính năng vừa được đề cập, giờ đây ứng dụng có thể tự định cấu hình phù hợp với đầu ra độ trễ thấp hơn trên các thiết bị có xác nhận hỗ trợ.
Đối với Android 4.2 (API cấp 17) trở xuống, cần có số lượng bộ đệm từ 2 trở lên để có độ trễ thấp hơn. Kể từ Android 4.3 (API cấp 18), một bộ đệm là đủ để đáp ứng độ trễ thấp hơn.
Tất cả giao diện OpenSL ES dành cho hiệu ứng đầu ra đều loại bỏ đường dẫn độ trễ thấp hơn.
Trình tự được đề xuất như sau:
- Kiểm tra API cấp 9 trở lên để xác nhận việc sử dụng OpenSL ES.
- Kiểm tra tính năng
android.hardware.audio.low_latency
bằng cách sử dụng mã như sau:Kotlin
import android.content.pm.PackageManager ... val pm: PackageManager = context.packageManager val claimsFeature: Boolean = pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY)
Java
import android.content.pm.PackageManager; ... PackageManager pm = getContext().getPackageManager(); boolean claimsFeature = pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY);
- Kiểm tra API cấp 17 trở lên để xác nhận việc sử dụng
android.media.AudioManager.getProperty()
. - Lấy tốc độ lấy mẫu đầu ra gốc hoặc tối ưu cũng như dung lượng bộ nhớ đệm dành cho luồng đầu ra chính của thiết bị này bằng cách sử dụng mã như sau:
Kotlin
import android.media.AudioManager ... val am = getSystemService(Context.AUDIO_SERVICE) as AudioManager val sampleRate: String = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE) val framesPerBuffer: String = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER)
Java
import android.media.AudioManager; ... AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE); String sampleRate = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); String framesPerBuffer = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
sampleRate
vàframesPerBuffer
là các chuỗi. Trước tiên, hãy kiểm tra giá trị rỗng (null), sau đó chuyển đổi sang int bằngInteger.parseInt()
. - Bây giờ, hãy sử dụng OpenSL ES để tạo một AudioPlayer có trình định vị dữ liệu hàng đợi bộ đệm PCM.
Lưu ý: Bạn có thể sử dụng ứng dụng kiểm thử Audio Buffer Size (Dung lượng bộ đệm âm thanh) để xác định dung lượng bộ nhớ đệm gốc và tốc độ lấy mẫu của các ứng dụng âm thanh OpenSL ES trên thiết bị âm thanh của bạn. Bạn cũng có thể truy cập GitHub để xem các mẫu audio-buffer-size.
Số lượng trình phát âm thanh có độ trễ thấp hơn bị giới hạn. Nếu ứng dụng cần nhiều hơn một vài nguồn âm thanh, hãy cân nhắc việc kết hợp âm thanh ở cấp ứng dụng. Hãy nhớ huỷ bỏ trình phát âm thanh khi hoạt động bị tạm dừng vì đây là tài nguyên chung được chia sẻ với các ứng dụng khác.
Để tránh các trục trặc về âm thanh, hàng đợi bộ đệm của trình xử lý gọi lại phải thực thi trong một khoảng thời gian ngắn có thể dự đoán được. Thường thì điều này ngụ ý rằng sẽ không có quy tắc chặn không giới hạn trên các mutex, điều kiện hoặc hoạt động I/O. Thay vào đó, hãy cân nhắc các thuật toán thử khoá (try lock), khoá (lock) và chờ (wait) bằng thời gian chờ, cũng như các thuật toán không chặn.
Quá trình tính toán cần thiết để kết xuất bộ đệm tiếp theo (dành cho AudioPlayer) hoặc sử dụng bộ đệm trước đó (dành cho AudioRecord) sẽ mất khoảng thời gian xấp xỉ như nhau đối với mỗi lệnh gọi lại. Hãy tránh sử dụng các thuật toán thực thi trong khoảng thời gian không xác định hoặc gây ra sự bùng nổ (bursty) trong các phép tính. Một phép tính lệnh gọi lại bị bùng nổ (bursty) nếu thời gian CPU dành cho một lệnh gọi lại nhất định lớn hơn đáng kể so với mức trung bình. Tóm lại, điều kiện lý tưởng là thời gian thực thi CPU của trình xử lý có phương sai gần bằng 0 và trình xử lý không bị chặn trong thời gian không giới hạn.
Âm thanh có độ trễ thấp hơn chỉ dành cho các đầu ra sau:
- Loa trên thiết bị.
- Tai nghe có dây.
- Bộ tai nghe và micrô có dây.
- Cổng âm thanh đầu ra.
- Âm thanh kỹ thuật số qua USB.
Trên một số thiết bị, độ trễ của loa cao hơn các đường dẫn khác do phải xử lý tín hiệu kỹ thuật số để hiệu chỉnh và bảo vệ loa.
Kể từ Android 5.0 (API cấp 21), tính năng đầu vào âm thanh
có độ trễ thấp hơn được hỗ trợ trên một số thiết bị chọn lọc. Để tận dụng tính năng này, trước tiên, hãy xác nhận rằng bạn có thể sử dụng đầu ra độ trễ thấp hơn theo mô tả ở trên. Khả năng xuất đầu ra độ trễ thấp hơn là điều kiện tiên quyết để sử dụng tính năng đầu vào độ trễ thấp hơn. Sau đó, hãy tạo một AudioRecorder với tốc độ lấy mẫu và dung lượng bộ nhớ đệm bằng với các thông số sẽ được sử dụng cho đầu ra. Giao diện OpenSL ES cho hiệu ứng đầu vào sẽ loại trừ đường dẫn độ trễ thấp hơn. Bạn phải sử dụng giá trị đặt trước SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION
của bản ghi để có độ trễ thấp hơn. Giá trị đặt trước này sẽ tắt chế độ xử lý tín hiệu kỹ thuật số theo từng thiết bị. Điều này có thể làm tăng độ trễ của đường dẫn đầu vào.
Để biết thêm thông tin về các giá trị đặt trước của bản ghi, hãy xem nội dung Giao diện cấu hình Android ở trên.
Đối với đầu vào và đầu ra đồng thời, mỗi bên dùng một trình xử lý hoàn thành hàng đợi bộ đệm riêng biệt. Không thể đảm bảo thứ tự tương đối của các lệnh gọi lại này hoặc tính năng đồng bộ hoá các xung nhịp âm thanh, ngay cả khi cả hai bên sử dụng cùng một tốc độ lấy mẫu. Ứng dụng của bạn nên lưu dữ liệu vào bộ đệm bằng tính năng đồng bộ hoá bộ đệm thích hợp.
Các xung nhịp âm thanh độc lập tiềm ẩn có một hệ quả là cần phải chuyển đổi tốc độ lấy mẫu không đồng bộ. Kỹ thuật đơn giản (mặc dù không lý tưởng cho chất lượng âm thanh) để chuyển đổi tốc độ lấy mẫu không đồng bộ là sao chép (hoặc bỏ các mẫu khi cần thiết) gần một điểm về 0 (zero-crossing point). Bạn cũng có thể áp dụng các kỹ thuật chuyển đổi phức tạp hơn.
Chế độ hiệu suất
Kể từ Android 7.1 (API cấp 25), OpenSL ES đã đưa ra một cách để chỉ định chế độ hiệu suất cao dành cho đường dẫn âm thanh. Có các lựa chọn sau:
SL_ANDROID_PERFORMANCE_NONE
: Không có yêu cầu cụ thể về hiệu suất. Chấp nhận hiệu ứng phần cứng và phần mềm.SL_ANDROID_PERFORMANCE_LATENCY
: Ưu tiên độ trễ. Không có hiệu ứng phần cứng hay phần mềm. Đây là chế độ mặc định.SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS
: Ưu tiên độ trễ nhưng vẫn chấp nhận hiệu ứng phần cứng và phần mềm.SL_ANDROID_PERFORMANCE_POWER_SAVING
: Ưu tiên việc tiết kiệm pin. Chấp nhận hiệu ứng phần cứng và phần mềm.
Lưu ý: Nếu không cần đến đường dẫn độ trễ thấp và muốn tận dụng các hiệu ứng âm thanh tích hợp sẵn trên thiết bị (ví dụ: để cải thiện chất lượng âm thanh khi phát video), thì bạn phải đặt rõ ràng chế độ hiệu suất cao thành SL_ANDROID_PERFORMANCE_NONE
.
Để đặt chế độ hiệu suất cao, bạn phải gọi SetConfiguration
bằng giao diện cấu hình Android, như minh hoạ dưới đây:
// Obtain the Android configuration interface using a previously configured SLObjectItf. SLAndroidConfigurationItf configItf = nullptr; (*objItf)->GetInterface(objItf, SL_IID_ANDROIDCONFIGURATION, &configItf); // Set the performance mode. SLuint32 performanceMode = SL_ANDROID_PERFORMANCE_NONE; result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_PERFORMANCE_MODE, &performanceMode, sizeof(performanceMode));
Tính bảo mật và quyền truy cập
Tính bảo mật trong Android được triển khai ở cấp quy trình, theo chừng mực "ai có thể làm gì". Mã ngôn ngữ lập trình Java không có nhiều chức năng hơn mã gốc và mã gốc cũng không có nhiều chức năng hơn mã ngôn ngữ lập trình Java. Sự khác biệt duy nhất giữa hai loại mã này là các API hiện có.
Các ứng dụng sử dụng OpenSL ES phải yêu cầu các quyền cần thiết cho các API tương tự không phải dạng gốc (non-native). Ví dụ: nếu ứng dụng của bạn ghi âm, thì ứng dụng cần quyền android.permission.RECORD_AUDIO
. Ứng dụng sử dụng hiệu ứng âm thanh cần có android.permission.MODIFY_AUDIO_SETTINGS
. Ứng dụng phát tài nguyên URI mạng cần có android.permission.NETWORK
. Để biết thêm thông tin, hãy xem nội dung Xử lý các quyền hệ thống.
Tuỳ thuộc vào phiên bản nền tảng và quy trình triển khai, các trình phân tích cú pháp nội dung phương tiện (media content parser) và phần mềm mã hoá/giải mã (codec) có thể chạy trong ngữ cảnh của ứng dụng Android sẽ gọi OpenSL ES (các bộ mã hoá và giải mã phần cứng được trừu tượng hoá nhưng phụ thuộc vào thiết bị). Nội dung không đúng định dạng được thiết kế để khai thác lỗ hổng của bộ mã hoá/giải mã và trình phân tích cú pháp là một vectơ tấn công đã được xác định. Bạn chỉ nên phát nội dung phương tiện qua các nguồn đáng tin cậy hoặc phân vùng ứng dụng để phần mã xử lý nội dung phương tiện qua các nguồn không đáng tin cậy chạy trong môi trường hộp cát (sandbox) tương đối. Ví dụ: bạn có thể xử lý nội dung phương tiện qua các nguồn không đáng tin cậy trong một quy trình riêng. Mặc dù cả hai quy trình vẫn chạy trong cùng một UID, nhưng sự phân tách này khiến việc tấn công trở nên khó khăn hơn.