Khi bạn đọc tài liệu Hộp cát về quyền riêng tư trên Android, hãy sử dụng nút Bản dùng thử cho nhà phát triển hoặc Beta để chọn phiên bản chương trình bạn đang làm việc, vì hướng dẫn có thể khác nhau.
Thời gian chạy SDK cho phép SDK chạy trong một hộp cát riêng, tách biệt với ứng dụng gọi. Thời gian chạy SDK cung cấp các biện pháp bảo vệ và bảo đảm nâng cao cho việc thu thập dữ liệu người dùng. Việc này được thực hiện thông qua môi trường thực thi đã sửa đổi nhằm giới hạn quyền truy cập dữ liệu và tập hợp các quyền được phép. Tìm hiểu thêm về Thời gian chạy SDK trong đề xuất thiết kế.
Các bước trên trang này sẽ hướng dẫn bạn thực hiện quy trình tạo SDK hỗ trợ thời gian chạy để xác định khung hiển thị dựa trên nền tảng web có thể kết xuất từ xa vào ứng dụng gọi.
Các hạn chế đã biết
Để biết danh sách các tính năng đang trong quá trình phát triển cho Thời gian chạy SDK, vui lòng xem ghi chú phát hành.
Chúng tôi dự kiến sẽ khắc phục các hạn chế sau đây trong bản phát hành chính tiếp theo cho nền tảng Android.
- Hiển thị quảng cáo trong khung hiển thị có thể cuộn. Ví dụ:
RecyclerView
không hoạt động đúng cách.- Bạn có thể gặp phải hiện tượng giật khi thay đổi kích thước.
- Các sự kiện cuộn bằng thao tác chạm của người dùng sẽ không được chuyển đúng cách đến thời gian chạy.
- Storage API (API Bộ nhớ)
- Không có bộ nhớ cho mỗi SDK trong Android 13.
Chúng tôi sẽ khắc phục sự cố sau đây vào năm 2023:
- API
getAdId
vàgetAppSetId
chưa hoạt động đúng cách vì chức năng hỗ trợ những API này chưa được kích hoạt.
Trước khi bắt đầu
Trước khi bắt đầu, hãy hoàn thành các bước sau:
Thiết lập môi trường phát triển của bạn cho Hộp cát về quyền riêng tư trên Android. Công cụ hỗ trợ Thời gian chạy SDK đang phát triển nhanh chóng, nên hướng dẫn này sẽ yêu cầu bạn sử dụng phiên bản Canary mới nhất của Android Studio. Bạn có thể chạy phiên bản Android Studio này song song với các phiên bản khác mà bạn sử dụng. Vì vậy, vui lòng cho chúng tôi biết nếu yêu cầu này không phù hợp với bạn.
Hãy cài đặt hình ảnh hệ thống trên một thiết bị được hỗ trợ hoặc thiết lập một trình mô phỏng có hỗ trợ Hộp cát về quyền riêng tư trên Android.
Thiết lập dự án trong Android Studio
Để dùng thử Thời gian chạy SDK, hãy sử dụng một mô hình tương tự như mô hình máy khách – máy chủ. Điểm khác biệt chính yếu là các ứng dụng (máy khách) và SDK ("máy chủ") chạy trên cùng một thiết bị.
- Thêm một mô-đun ứng dụng vào dự án. Mô-đun này đóng vai trò ứng dụng điều khiển SDK.
- Trong mô-đun ứng dụng, hãy bật Thời gian chạy SDK, khai báo các quyền cần thiết và định cấu hình các dịch vụ quảng cáo cho API cụ thể.
- Thêm một mô-đun thư viện vào dự án. Mô-đun này có chứa mã SDK của bạn.
- Trong mô-đun SDK, hãy khai báo các quyền cần thiết. Bạn không cần định cấu hình các dịch vụ quảng cáo dành riêng cho API trong mô-đun này.
- Xoá
dependencies
trong tệpbuild.gradle
của mô-đun thư viện mà SDK của bạn không sử dụng. Trong hầu hết mọi trường hợp, bạn có thể xoá tất cả các phần phụ thuộc. Bạn có thể thực hiện việc này bằng cách tạo một thư mục mới có tên tương ứng với SDK của bạn. Tạo thủ công một mô-đun mới bằng loại
com.android.privacy-sandbox-sdk
. Mô-đun này đi kèm với mã SDK để tạo một APK có thể triển khai cho thiết bị của bạn. Bạn có thể thực hiện việc này bằng cách tạo một thư mục mới có tên tương ứng với SDK của bạn. Thêm một tệpbuild.gradle
trống. Nội dung của tệp này sẽ được điền sau trong hướng dẫn này.Thêm đoạn mã sau vào tệp
gradle.properties
:android.experimental.privacysandboxsdk.enable=true
Tải hình ảnh trình mô phỏng TiramisuPrivacySandbox xuống rồi tạo một trình mô phỏng với hình ảnh này chứa Cửa hàng Play.
Tuỳ thuộc vào việc bạn là nhà phát triển SDK hay nhà phát triển ứng dụng, bạn có thể có cách thiết lập cuối cùng khác với cách thiết lập đã mô tả trong đoạn trước.
Dùng Android Studio hoặc Cầu gỡ lỗi Android (ADB) để cài đặt SDK trên thiết bị kiểm thử, tương tự như cách bạn cài đặt ứng dụng. Để giúp bạn bắt đầu, chúng tôi đã tạo các ứng dụng mẫu bằng ngôn ngữ lập trình Kotlin và Java. Bạn có thể tìm thấy các ứng dụng này trong kho lưu trữ GitHub. Tệp README và tệp kê khai có nhận xét mô tả những gì cần thay đổi để chạy mẫu trong các phiên bản Android Studio ổn định.
Chuẩn bị SDK của bạn
Tạo thư mục ở cấp mô-đun theo cách thủ công. Thư mục này đóng vai trò là trình bao bọc xung quanh mã triển khai của bạn để tạo APK SDK. Trong thư mục mới, hãy thêm tệp
build.gradle
rồi điền đoạn mã sau vào tệp đó. Sử dụng một tên riêng biệt cho SDK hỗ trợ thời gian chạy (RE-SDK) và cung cấp một phiên bản. Đưa mô-đun thư viện của bạn vào mụcdependencies
.plugins { id 'com.android.privacy-sandbox-sdk' } android { compileSdkPreview 'TiramisuPrivacySandbox' minSdkPreview 'TiramisuPrivacySandbox' namespace = "com.example.example-sdk" bundle { packageName = "com.example.privacysandbox.provider" sdkProviderClassName = "com.example.sdk_implementation.SdkProviderImpl" setVersion(1, 0, 0) } } dependencies { include project(':<your-library-here>') }
Tạo một lớp trong thư viện triển khai để đóng vai trò là điểm truy cập cho SDK. Tên của lớp phải ánh xạ tới giá trị của
sdkProviderClassName
và mở rộngSandboxedSdkProvider
.
Điểm truy cập cho SDK của bạn sẽ mở rộng SandboxedSdkProvider
. SandboxedSdkProvider
chứa đối tượng Context
cho SDK mà bạn có thể truy cập bằng cách gọi getContext()
. Bạn chỉ có thể truy cập vào ngữ cảnh này sau khi gọi onLoadSdk()
.
Để biên dịch ứng dụng SDK, bạn cần phải ghi đè lên các phương thức để xử lý vòng đời SDK:
onLoadSdk()
Tải SDK vào hộp cát, đồng thời thông báo cho ứng dụng gọi khi SDK đã sẵn sàng xử lý yêu cầu bằng cách truyền giao diện của SDK dưới dạng đối tượng
IBinder
được bao bọc bên trong đối tượngSandboxedSdk
mới. Hướng dẫn về dịch vụ ràng buộc giới thiệu nhiều cách để cung cấpIBinder
. Bạn có thể linh hoạt chọn cách cung cấp nhưng phải nhất quán với SDK và ứng dụng gọi.Lấy AIDL làm ví dụ, bạn nên xác định tệp AIDL để hiển thị
IBinder
mà ứng dụng sẽ chia sẻ và sử dụng:// ISdkInterface.aidl interface ISdkInterface { // the public functions to share with the App. int doSomething(); }
getView()
Tạo và thiết lập khung hiển thị cho quảng cáo của bạn, khởi tạo khung hiển thị giống như mọi khung hiển thị khác của Android và trả về khung hiển thị để kết xuất từ xa trong một cửa sổ có chiều rộng và chiều cao cho trước tính bằng pixel.
Đoạn mã sau đây minh hoạ cách ghi đè lên các phương thức này:
Kotlin
class SdkProviderImpl : SandboxedSdkProvider() { override fun onLoadSdk(params: Bundle?): SandboxedSdk { // Returns a SandboxedSdk, passed back to the client. The IBinder used // to create the SandboxedSdk object is used by the app to call into the // SDK. return SandboxedSdk(SdkInterfaceProxy()) } override fun getView(windowContext: Context, bundle: Bundle, width: Int, height: Int): View { val webView = WebView(windowContext) val layoutParams = LinearLayout.LayoutParams(width, height) webView.setLayoutParams(layoutParams) webView.loadUrl("https://developer.android.com/privacy-sandbox") return webView } private class SdkInterfaceProxy : ISdkInterface.Stub() { fun doSomething() { // Implementation of the API. } } }
Java
public class SdkProviderImpl extends SandboxedSdkProvider { @Override public SandboxedSdk onLoadSdk(Bundle params) { // Returns a SandboxedSdk, passed back to the client. The IBinder used // to create the SandboxedSdk object is used by the app to call into the // SDK. return new SandboxedSdk(new SdkInterfaceProxy()); } @Override public View getView(Context windowContext, Bundle bundle, int width, int height) { WebView webView = new WebView(windowContext); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(width, height); webView.setLayoutParams(layoutParams); webView.loadUrl("https://developer.android.com/privacy-sandbox"); return webView; } private static class SdkInterfaceProxy extends ISdkInterface.Stub { @Override public void doSomething() { // Implementation of the API. } } }
SdkSandboxController
SdkSandboxController
là một trình bao bọc dịch vụ hệ thống dựa trên bối cảnh có sẵn cho SDK. SDK có thể tìm nạp trình bao bọc này bằng cách sử dụng bối cảnh nhận được từ SandboxedSdkProvider#getContext()
và gọi context.getSystemService(SdkSandboxController.class)
trên bối cảnh này. Bộ điều khiển có các API giúp SDK tương tác và lấy thông tin từ Hộp cát về quyền riêng tư.
Hầu hết các API trong bộ điều khiển đều gửi ngoại lệ nếu SandboxedSdkContext
không phải là bối cảnh dùng để truy cập vào các API đó. Chúng tôi sẽ lên kế hoạch cung cấp trình bao bọc dịch vụ cho các bối cảnh khác. SdkSandboxController
dành cho các nhà cung cấp SDK và không được các nhà phát triển ứng dụng khuyên dùng.
Khả năng giao tiếp giữa các SDK
Các SDK trong thời gian chạy phải có khả năng giao tiếp với nhau để hỗ trợ các trường hợp dàn xếp và trường hợp sử dụng liên quan. Bộ công cụ được mô tả ở đây cung cấp một giao diện để hoạt động với các SDK được cung cấp cho các SDK khác trong hộp cát. Phương thức triển khai Thời gian chạy SDK này là bước đầu tiên để SDK giao tiếp với SDK và có thể chưa bao gồm mọi trường hợp sử dụng cho tính năng dàn xếp trong Hộp cát về quyền riêng tư.
API getSandboxedSdks()
trong SdkSandboxController
cung cấp một lớp SandboxedSdk
cho tất cả các SDK đã tải trong Hộp cát về quyền riêng tư. Đối tượng SandboxedSdk
có thông tin chi tiết về SDK và một sdkInterface
để ứng dụng giao tiếp với SDK đó.
SDK trong Hộp cát về quyền riêng tư dự kiến sẽ sử dụng các đoạn mã tương tự như sau để trao đổi với các SDK khác. Giả sử mã này được viết để cho phép "SDK1" giao tiếp với "SDK2", cả hai đều được ứng dụng tải trong Hộp cát về quyền riêng tư.
SdkSandboxController controller = mSdkContext
.getSystemService(SdkSandboxController.class);
List<SandboxedSdk> sandboxedSdks = controller.getSandboxedSdks();
SandboxedSdk sdk2 = sandboxedSdks.stream().filter( // The SDK it wants to
// connect to, based on SDK name or SharedLibraryInfo.
try {
IBinder binder = sdk2.getInterface();
ISdkApi sdkApi = ISdkApi.Stub.asInterface(binder);
// Call API on SDK2
message = sdkApi.getMessage();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
Trong ví dụ trên, SDK1 thêm thư viện AIDL của SDK2 làm phần phụ thuộc. SDK ứng dụng này chứa mã Binder do AIDL tạo. Cả hai SDK sẽ xuất thư viện AIDL này. Điều này giống với những gì ứng dụng được dự kiến sẽ thực hiện khi chúng giao tiếp với SDK trong Hộp cát về quyền riêng tư.
Chúng tôi sẽ hỗ trợ thêm cách tạo tự động để chia sẻ giao diện giữa các SDK trong bản cập nhật sau này.
Các SDK trong thời gian chạy có thể cần phải giao tiếp với các phần phụ thuộc của ứng dụng và SDK quảng cáo chưa hỗ trợ thời gian chạy.
API registerAppOwnedSdkSandboxInterface()
trong SdkSandboxManager
cung cấp một cách để các SDK chưa hỗ trợ thời gian chạy đăng ký giao diện với nền tảng. API getAppOwnedSdkSandboxInterfaces()
trong SdkSandboxController
cung cấp AppOwnedSdkSandboxInterface
cho tất cả các SDK đã đăng ký, được liên kết tĩnh.
Ví dụ sau đây minh hoạ cách đăng ký các giao diện để có thể giao tiếp với SDK hỗ trợ thời gian chạy:
// Register AppOwnedSdkSandboxInterface
mSdkSandboxManager.registerAppOwnedSdkSandboxInterface(
new AppOwnedSdkSandboxInterface(
APP_OWNED_SDK_NAME, (long) APP_OWNED_SDK_VERSION, new AppOwnedSdkApi())
);
Ví dụ sau minh hoạ cách đo lường hoạt động giao tiếp với SDK chưa hỗ trợ thời gian chạy:
// Get AppOwnedSdkSandboxInterface
List<AppOwnedSdkSandboxInterface> appOwnedSdks = mSdkContext
.getSystemService(SdkSandboxController.class)
.getAppOwnedSdkSandboxInterfaces();
AppOwnedSdkSandboxInterface appOwnedSdk = appOwnedSdks.stream()
.filter(s -> s.getName().contains(APP_OWNED_SDK_NAME))
.findAny()
.get();
IAppOwnedSdkApi appOwnedSdkApi =
IAppOwnedSdkApi.Stub.asInterface(appOwnedSdk.getInterface());
message = appOwnedSdkApi.getMessage();
Vì các SDK quảng cáo chưa hỗ trợ thời gian chạy có thể không tự đăng ký được, nên chúng tôi đề xuất tạo một SDK dàn xếp để xử lý việc đăng ký và đưa các SDK của đối tác hoặc SDK ứng dụng vào dưới dạng phần phụ thuộc trực tiếp. SDK dàn xếp này thiết lập hoạt động giao tiếp giữa SDK và các phần phụ thuộc không hỗ trợ thời gian chạy và trình dàn xếp hỗ trợ thời gian chạy hoạt động như một bộ chuyển đổi.
Hỗ trợ hoạt động
Các SDK hỗ trợ thời gian chạy không thể thêm thẻ hoạt động vào tệp kê khai và không thể trực tiếp bắt đầu các hoạt động của riêng chúng. Quyền truy cập được cung cấp cho đối tượng Activity
bằng cách đăng ký SdkSandboxActivityHandler
và bắt đầu hoạt động trong hộp cát:
1. Đăng ký một SdkSandboxActivityHandler
Đăng ký một thực thể của SdkSandboxActivityHandler
bằng SdkSandboxController#registerSdkSandboxActivityHandler(SandboxedActivityHandler)
API đăng ký đối tượng đó và trả về đối tượng IBinder
giúp nhận dạng SdkSandboxActivityHandler
đã truyền.
public interface SdkSandboxActivityHandler {
void onActivityCreated(Activity activity);
}
API đăng ký đối tượng đó và trả về đối tượng IBinder
giúp nhận dạng SdkSandboxActivityHandler
đã truyền.
2. Bắt đầu hoạt động trong Hộp cát
SDK sẽ truyền mã thông báo được trả về để xác định SdkSandboxActivityHandler
đã đăng ký cho ứng dụng. Sau đó, ứng dụng này sẽ gọi SdkSandboxManager#startSdkSandboxActivity(Activity, Binder)
, truyền một hoạt động cần bắt đầu trong Hộp cát và một mã thông báo xác định SdkSandboxActivityHandler
đã đăng ký.
Bước này bắt đầu một hoạt động mới trên nền tảng chạy trong cùng một Thời gian chạy SDK với SDK yêu cầu.
Khi hoạt động bắt đầu, SDK sẽ nhận được thông báo thông qua lệnh gọi đến SdkSandboxActivityHandler#onActivityCreated(Activity)
trong quá trình thực thi Activity#OnCreate(Bundle)
.
Ví dụ: với quyền truy cập vào đối tượng Activity
, phương thức gọi có thể đặt contentView
cho khung hiển thị bằng cách gọi Activity#setContentView(View)
.
Để đăng ký các phương thức gọi lại trong vòng đời, hãy sử dụng Activity#registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks)
.
Để đăng ký OnBackInvokedCallback
cho hoạt động đã truyền, hãy sử dụng Activity#getOnBackInvokedDispatcher().registerOnBackInvokedCallback(Int,
OnBackInvokedCallback)
.
Kiểm thử trình phát video trong Thời gian chạy SDK
Ngoài việc hỗ trợ quảng cáo biểu ngữ, Hộp cát về quyền riêng tư còn cam kết hỗ trợ các trình phát video chạy trong Thời gian chạy SDK.
Quy trình thử nghiệm trình phát video tương tự như thử nghiệm quảng cáo biểu ngữ. Thay đổi phương thức
getView()
của điểm truy cập SDK của bạn để đưa trình phát video vào đối tượng View
được trả về. Kiểm thử tất cả các luồng của trình phát video mà bạn mong muốn Hộp cát về quyền riêng tư hỗ trợ. Lưu ý rằng hoạt động giao tiếp giữa SDK và ứng dụng về vòng đời của video hiện nằm ngoài phạm vi, do đó, chưa cần phải đưa ra ý kiến phản hồi về chức năng này.
Hoạt động kiểm thử và ý kiến phản hồi của bạn sẽ đảm bảo Thời gian chạy SDK hỗ trợ tất cả các trường hợp sử dụng của trình phát video mà bạn ưa thích.
Đoạn mã sau đây minh hoạ cách trả về một lượt xem video đơn giản tải từ một URL.
Kotlin
class SdkProviderImpl : SandboxedSdkProvider() { override fun getView(windowContext: Context, bundle: Bundle, width: Int, height: Int): View { val videoView = VideoView(windowContext) val layoutParams = LinearLayout.LayoutParams(width, height) videoView.setLayoutParams(layoutParams) videoView.setVideoURI(Uri.parse("https://test.website/video.mp4")) videoView.setOnPreparedListener { mp -> mp.start() } return videoView } }
Java
public class SdkProviderImpl extends SandboxedSdkProvider { @Override public View getView(Context windowContext, Bundle bundle, int width, int height) { VideoView videoView = new VideoView(windowContext); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(width, height); videoView.setLayoutParams(layoutParams); videoView.setVideoURI(Uri.parse("https://test.website/video.mp4")); videoView.setOnPreparedListener(mp -> { mp.start(); }); return videoView; } }
Sử dụng API lưu trữ trong SDK của bạn
Các SDK trong Thời gian chạy SDK không còn truy cập, đọc hoặc ghi trong bộ nhớ trong của ứng dụng nữa và ngược lại. Thời gian chạy SDK sẽ được phân bổ vào vùng bộ nhớ trong riêng biệt, đảm bảo tách biệt với ứng dụng.
Các SDK có thể truy cập vào bộ nhớ trong riêng biệt này thông qua các API lưu trữ tệp trên đối tượng Context
do SandboxedSdkProvider#getContext()
trả về. SDK chỉ có thể dùng bộ nhớ trong nên chỉ các API bộ nhớ trong, chẳng hạn như Context.getFilesDir()
hoặc Context.getCacheDir()
, mới hoạt động. Vui lòng tham khảo thêm ví dụ ở bài viết Quyền truy cập vào bộ nhớ trong.
Không hỗ trợ quyền truy cập vào bộ nhớ ngoài trong Thời gian chạy SDK. Việc gọi các API để truy cập vào bộ nhớ ngoài sẽ gửi một ngoại lệ hoặc trả về null
. Dưới đây là một số ví dụ:
- Việc truy cập các tệp bằng Khung truy cập bộ nhớ sẽ tạo ra
SecurityException
. getExternalFilsDir()
sẽ luôn trả vềnull
.
Trong Android 13, tất cả các SDK trong Thời gian chạy SDK sẽ dùng chung bộ nhớ trong được phân bổ cho Thời gian chạy SDK. Bộ nhớ sẽ được duy trì cho đến khi gỡ cài đặt, hoặc xoá dữ liệu của ứng dụng.
Bạn phải sử dụng Context
do SandboxedSdkProvider.getContext()
trả về để lưu trữ. Việc sử dụng API lưu trữ tệp trên mọi thực thể đối tượng Context
nào khác, chẳng hạn như ngữ cảnh của ứng dụng, không được đảm bảo sẽ hoạt động như mong đợi trong mọi tình huống hoặc trong tương lai.
Đoạn mã sau đây minh hoạ cách sử dụng bộ nhớ trong Thời gian chạy SDK:
Kotlin
private static class SdkInterfaceStorage extends ISdkInterface.Stub { override fun doSomething() { val filename = "myfile" val fileContents = "content" try { getContext().openFileOutput(filename, Context.MODE_PRIVATE).use { it.write(fileContents.toByteArray()) } catch (e: Exception) { throw RuntimeException(e) } } } }
Java
private static class SdkInterfaceStorage extends ISdkInterface.Stub { @Override public void doSomething() { final filename = "myFile"; final String fileContents = "content"; try (FileOutputStream fos = getContext().openFileOutput(filename, Context.MODE_PRIVATE)) { fos.write(fileContents.toByteArray()); } catch (Exception e) { throw new RuntimeException(e); } } }
Bộ nhớ trên mỗi SDK
Ở bộ nhớ trong riêng biệt của từng Thời gian chạy SDK, mỗi SDK có một thư mục bộ nhớ riêng. Bộ nhớ trên mỗi SDK là sự phân tách theo logic bộ nhớ trong của Thời gian chạy SDK, giúp tính toán dung lượng bộ nhớ mà mỗi SDK sử dụng.
Trên Android 13, chỉ có một API trả về đường dẫn đến bộ nhớ trên mỗi SDK: Context#getDataDir()
.
Trên Android 14, tất cả các Internal Storage API (API Bộ nhớ trong) trên đối tượng Context
đều trả về một đường dẫn bộ nhớ cho từng SDK. Bạn có thể cần bật tính năng này bằng cách chạy lệnh adb sau đây:
adb shell device_config put adservices sdksandbox_customized_sdk_context_enabled true
SharedPreferences
của ứng dụng đọc
Ứng dụng khách có thể chọn chia sẻ một tập hợp các khoá trên SharedPreferences
với SdkSandbox
. SDK có thể đọc dữ liệu đã đồng bộ hoá từ ứng dụng thông qua API SdkSanboxController#getClientSharedPreferences()
. SharedPreferences
mà API này trả về chỉ dành cho mục đích đọc. Bạn không nên ghi vào đó.
Truy cập vào mã nhận dạng cho quảng cáo do Dịch vụ Google Play cung cấp
Nếu SDK của bạn cần quyền truy cập vào mã nhận dạng cho quảng cáo do Dịch vụ Google Play cung cấp:
- Khai báo quyền
android.permission.ACCESS_ADSERVICES_AD_ID
trong tệp kê khai của SDK. - Sử dụng
AdIdManager#getAdId()
để truy xuất giá trị không đồng bộ.
Truy cập vào mã nhóm ứng dụng do Dịch vụ Google Play cung cấp
Nếu SDK của bạn cần quyền truy cập vào mã nhóm ứng dụng do Dịch vụ Google Play cung cấp:
- Sử dụng
AppSetIdManager#getAppSetId()
để truy xuất giá trị không đồng bộ.
Cập nhật ứng dụng
Để gọi vào một SDK đang chạy trong Thời gian chạy SDK, hãy thực hiện những thay đổi sau đây đối với ứng dụng khách dùng để gọi:
Thêm quyền
INTERNET
vàACCESS_NETWORK_STATE
vào tệp kê khai của ứng dụng:<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
Trong hoạt động của ứng dụng bao gồm một quảng cáo, hãy khai báo thông tin tham chiếu đến
SdkSandboxManager
(là boolean cho biết liệu SDK có được tải hay không) và một đối tượngSurfaceView
để kết xuất từ xa:Kotlin
private lateinit var mSdkSandboxManager: SdkSandboxManager private lateinit var mClientView: SurfaceView private var mSdkLoaded = false companion object { private const val SDK_NAME = "com.example.privacysandbox.provider" }
Java
private static final String SDK_NAME = "com.example.privacysandbox.provider"; private SdkSandboxManager mSdkSandboxManager; private SurfaceView mClientView; private boolean mSdkLoaded = false;
Kiểm tra xem thiết bị có quy trình Thời gian chạy SDK hay không.
Kiểm tra hằng số
SdkSandboxState
(getSdkSandboxState()
).SDK_SANDBOX_STATE_ENABLED_PROCESS_ISOLATION
có nghĩa là Thời gian chạy SDK có sẵn.Kiểm tra để đảm bảo rằng đã gọi
loadSdk()
thành công. Nếu không có ngoại lệ nào được gửi thì nghĩa là đã gọi thành công, và trình thu nhận là thực thể củaSandboxedSdk
.Gọi
loadSdk()
từ nền trước. Nếu được gọi từ nền, hệ thống sẽ gửiSecurityException
.Kiểm tra
OutcomeReceiver
cho thực thể củaSandboxedSdk
để xác minh xem có xảy raLoadSdkException
hay không. Nếu xảy ra ngoại lệ thì có thể Thời gian chạy SDK không có sẵn.
Nếu lệnh gọi
SdkSandboxState
hoặcloadSdk
không thành công, thì Thời gian chạy SDK không có sẵn và lệnh gọi sẽ quay lại SDK hiện có.Xác định lớp gọi lại bằng cách triển khai
OutcomeReceiver
để tương tác với SDK trong thời gian chạy sau khi được tải. Trong ví dụ sau, máy khách sử dụng lệnh gọi lại để chờ đến khi SDK được tải thành công, sau đó cố gắng kết xuất khung hiển thị web từ SDK. Các lệnh gọi lại được xác định sau trong bước này.Kotlin
private inner class LoadSdkOutcomeReceiverImpl private constructor() : OutcomeReceiver<SandboxedSdk?, LoadSdkException?> { override fun onResult(sandboxedSdk: SandboxedSdk) { mSdkLoaded = true val binder: IBinder = sandboxedSdk.getInterface() if (!binderInterface.isPresent()) { // SDK is not loaded anymore. return } val sdkInterface: ISdkInterface = ISdkInterface.Stub.asInterface(binder) sdkInterface.doSomething() Handler(Looper.getMainLooper()).post { val bundle = Bundle() bundle.putInt(SdkSandboxManager.EXTRA_WIDTH_IN_PIXELS, mClientView.getWidth()) bundle.putInt(SdkSandboxManager.EXTRA_HEIGHT_IN_PIXELS, mClientView.getHeight()) bundle.putInt(SdkSandboxManager.EXTRA_DISPLAY_ID, display!!.displayId) bundle.putInt(SdkSandboxManager.EXTRA_HOST_TOKEN, mClientView.getHostToken()) mSdkSandboxManager!!.requestSurfacePackage( SDK_NAME, bundle, { obj: Runnable -> obj.run() }, RequestSurfacePackageOutcomeReceiverImpl()) } } override fun onError(error: LoadSdkException) { // Log or show error. } }
Java
import static android.app.sdksandbox.SdkSandboxManager.EXTRA_DISPLAY_ID; import static android.app.sdksandbox.SdkSandboxManager.EXTRA_HEIGHT_IN_PIXELS; import static android.app.sdksandbox.SdkSandboxManager.EXTRA_HOST_TOKEN; import static android.app.sdksandbox.SdkSandboxManager.EXTRA_WIDTH_IN_PIXELS; private class LoadSdkOutcomeReceiverImpl implements OutcomeReceiver<LoadSdkResponse, LoadSdkException> { private LoadSdkOutcomeReceiverImpl() {} @Override public void onResult(@NonNull SandboxedSdk sandboxedSdk) { mSdkLoaded = true; IBinder binder = sandboxedSdk.getInterface(); if (!binderInterface.isPresent()) { // SDK is not loaded anymore. return; } ISdkInterface sdkInterface = ISdkInterface.Stub.asInterface(binder); sdkInterface.doSomething(); new Handler(Looper.getMainLooper()).post(() -> { Bundle bundle = new Bundle(); bundle.putInt(EXTRA_WIDTH_IN_PIXELS, mClientView.getWidth()); bundle.putInt(EXTRA_HEIGHT_IN_PIXELS, mClientView.getHeight()); bundle.putInt(EXTRA_DISPLAY_ID, getDisplay().getDisplayId()); bundle.putInt(EXTRA_HOST_TOKEN, mClientView.getHostToken()); mSdkSandboxManager.requestSurfacePackage( SDK_NAME, bundle, Runnable::run, new RequestSurfacePackageOutcomeReceiverImpl()); }); } @Override public void onError(@NonNull LoadSdkException error) { // Log or show error. } }
Để quay lại khung hiển thị từ xa của SDK trong thời gian chạy khi đang gọi
requestSurfacePackage()
, hãy triển khai giao diệnOutcomeReceiver<Bundle, RequestSurfacePackageException>
:Kotlin
private inner class RequestSurfacePackageOutcomeReceiverImpl : OutcomeReceiver<Bundle, RequestSurfacePackageException> { fun onResult(@NonNull result: Bundle) { Handler(Looper.getMainLooper()) .post { val surfacePackage: SurfacePackage = result.getParcelable( EXTRA_SURFACE_PACKAGE, SurfacePackage::class.java) mRenderedView.setChildSurfacePackage(surfacePackage) mRenderedView.setVisibility(View.VISIBLE) } } fun onError(@NonNull error: RequestSurfacePackageException?) { // Error handling } }
Java
import static android.app.sdksandbox.SdkSandboxManager.EXTRA_SURFACE_PACKAGE; private class RequestSurfacePackageOutcomeReceiverImpl implements OutcomeReceiver<Bundle, RequestSurfacePackageException> { @Override public void onResult(@NonNull Bundle result) { new Handler(Looper.getMainLooper()) .post( () -> { SurfacePackage surfacePackage = result.getParcelable( EXTRA_SURFACE_PACKAGE, SurfacePackage.class); mRenderedView.setChildSurfacePackage(surfacePackage); mRenderedView.setVisibility(View.VISIBLE); }); } @Override public void onError(@NonNull RequestSurfacePackageException error) { // Error handling } }
Khi hiện xong khung hiển thị, hãy nhớ thả
SurfacePackage
bằng cách gọi:surfacePackage.notifyDetachedFromWindow()
Trong
onCreate()
, hãy khởi chạySdkSandboxManager
, các lệnh gọi lại cần thiết, sau đó đưa ra yêu cầu tải SDK:Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) mSdkSandboxManager = applicationContext.getSystemService( SdkSandboxManager::class.java ) mClientView = findViewById(R.id.rendered_view) mClientView.setZOrderOnTop(true) val loadSdkCallback = LoadSdkCallbackImpl() mSdkSandboxManager.loadSdk( SDK_NAME, Bundle(), { obj: Runnable -> obj.run() }, loadSdkCallback ) }
Java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSdkSandboxManager = getApplicationContext().getSystemService( SdkSandboxManager.class); mClientView = findViewById(R.id.rendered_view); mClientView.setZOrderOnTop(true); LoadSdkCallbackImpl loadSdkCallback = new LoadSdkCallbackImpl(); mSdkSandboxManager.loadSdk( SDK_NAME, new Bundle(), Runnable::run, loadSdkCallback); }
Ứng dụng có thể chọn chia sẻ một số khoá của
SharedPreferences
mặc định của ứng dụng đó với Hộp cát. Ứng dụng có thể làm vậy bằng cách gọi phương thứcSdkSandboxManager#addSyncedSharedPreferencesKeys(Set<String>keys)
trên bất cứ thực thể nào của trình quản lýSdkSandbox
. Sau khi ứng dụng thông báo choSdkSandboxManager
về các khoá cần đồng bộ hoá,SdkSandboxManager
sẽ đồng bộ hoá giá trị của các khoá đó vào hộp cát và sau đó, sdk có thể đọc các giá trị bằngSdkSandboxController#getClientSharedPreferences
. Hãy xem phần SharedPreferences của ứng dụng đọc để biết thêm thông tin.Tập hợp các khoá đang được đồng bộ hoá sẽ không được lưu trữ sau khi khởi động lại ứng dụng và dữ liệu được đồng bộ hoá với hộp cát sẽ bị xoá khi khởi động lại hộp cát. Do đó, ứng dụng cần phải bắt đầu đồng bộ hoá bằng cách gọi
addSyncedSharedPreferencesKeys
mỗi khi khởi động ứng dụng.Bạn có thể sửa đổi tập hợp các khoá đang được đồng bộ hoá bằng cách gọi
SdkSandboxManager#removeSyncedSharedPreferencesKeys(Set<String>keys)
để xoá khoá. Để xem tập hợp các khoá hiện đang được đồng bộ hoá, hãy dùngSdkSandboxManager#getSyncedSharedPreferencesKeys()
.Bạn nên giảm thiểu kích thước của tập hợp khoá và chỉ sử dụng khi cần thiết. Nếu bạn muốn chuyển thông tin sang SDK cho mục đích chung, hãy giao tiếp trực tiếp với SDK bằng giao diện
SandboxedSdk
của SDK đó. Trường hợp có thể phải dùng các API này đó là nếu ứng dụng đang dùng SDK Nền tảng quản lý sự đồng ý (CMP), thì SDK bên trong hộp cát sẽ muốn đọc các SDK CMP dữ liệu lưu trữ trongSharedPreferences
mặc định.Kotlin
override fun onCreate(savedInstanceState: Bundle?) { … // At some point, initiate the set of keys for synchronization with sandbox mSdkSandboxManager.addSyncedSharedPreferencesKeys(Set.of("foo", "bar")); }
Java
@Override protected void onCreate(Bundle savedInstanceState) { … // At some point, initiate the set of keys for synchronization with sandbox mSdkSandboxManager.addSyncedSharedPreferencesKeys(Set.of("foo", "bar")); }
Để xử lý trường hợp khi quy trình hộp cát SDK bị chấm dứt đột ngột, hãy xác định phương thức triển khai cho giao diện
SdkSandboxProcessDeathCallback
:Kotlin
private inner class SdkSandboxLifecycleCallbackImpl() : SdkSandboxProcessDeathCallback { override fun onSdkSandboxDied() { // The SDK runtime process has terminated. To bring back up the // sandbox and continue using SDKs, load the SDKs again. val loadSdkCallback = LoadSdkOutcomeReceiverImpl() mSdkSandboxManager.loadSdk( SDK_NAME, Bundle(), { obj: Runnable -> obj.run() }, loadSdkCallback) } }
Java
private class SdkSandboxLifecycleCallbackImpl implements SdkSandboxProcessDeathCallback { @Override public void onSdkSandboxDied() { // The SDK runtime process has terminated. To bring back up // the sandbox and continue using SDKs, load the SDKs again. LoadSdkOutcomeReceiverImpl loadSdkCallback = new LoadSdkOutcomeReceiverImpl(); mSdkSandboxManager.loadSdk( SDK_NAME, new Bundle(), Runnable::run, loadSdkCallback); } }
Để đăng ký lệnh gọi lại này nhằm nhận thông tin về thời điểm hộp cát SDK bị chấm dứt, hãy thêm dòng sau bất kỳ lúc nào:
Kotlin
mSdkSandboxManager.addSdkSandboxProcessDeathCallback({ obj: Runnable -> obj.run() }, SdkSandboxLifecycleCallbackImpl())
Java
mSdkSandboxManager.addSdkSandboxProcessDeathCallback(Runnable::run, new SdkSandboxLifecycleCallbackImpl());
Vì trạng thái của hộp cát bị mất khi quy trình xử lý của nó kết thúc, nên các chế độ xem do SDK kết xuất từ xa có thể không còn hoạt động chính xác nữa. Để tiếp tục tương tác với các SDK, các khung hiển thị này phải được tải lại để bắt đầu một quy trình hộp cát mới.
Thêm phần phụ thuộc trên mô-đun SDK vào
build.gradle
của ứng dụng khách:dependencies { ... implementation project(':<your-sdk-module>') ... }
Kiểm thử ứng dụng
Để chạy ứng dụng, hãy cài đặt ứng dụng SDK và ứng dụng trên thiết bị kiểm thử của bạn thông qua Android Studio hoặc dòng lệnh.
Triển khai thông qua Android Studio
Khi triển khai thông qua Android Studio, hãy hoàn thành các bước sau:
- Mở dự án Android Studio cho ứng dụng khách của bạn.
- Chuyển đến Run > Edit Configurations (Chạy > Chỉnh sửa cấu hình). Cửa sổ Run/Debug Configuration (Cấu hình chạy/gỡ lỗi) sẽ xuất hiện.
- Trong phần Launch Options (Tuỳ chọn khởi chạy), hãy đặt Launch (Khởi chạy) thành Specified Activity (Hoạt động đã chỉ định).
- Nhấp vào trình đơn có biểu tượng ba dấu chấm bên cạnh Activity (Hoạt động) rồi chọn Main Activity (Hoạt động chính) cho ứng dụng khách của bạn.
- Nhấp vào Apply (Áp dụng) rồi nhấp vào OK.
- Nhấp vào biểu tượng Run (Chạy)
để cài đặt ứng dụng và SDK trên thiết bị kiểm thử của bạn.
Triển khai trên dòng lệnh
Khi triển khai bằng dòng lệnh, hãy hoàn tất các bước trong danh sách sau.
Phần này giả định rằng tên mô-đun ứng dụng SDK của bạn là sdk-app
và tên mô-đun ứng dụng khách là client-app
.
Trên một thiết bị đầu cuối của dòng lệnh, hãy tạo các APK SDK Hộp cát về quyền riêng tư:
./gradlew :client-app:buildPrivacySandboxSdkApksForDebug
Thao tác này sẽ tạo ra vị trí cho các APK đã tạo. Các APK này được ký bằng khoá gỡ lỗi cục bộ của bạn. Bạn cần có đường dẫn này trong lệnh tiếp theo.
Cài đặt APK trên thiết bị:
adb install -t /path/to/your/standalone.apk
Trong Android Studio, hãy nhấp vào Run > Edit Configurations (Chạy > Chỉnh sửa cấu hình). Cửa sổ Run/Debug Configuration (Cấu hình chạy/gỡ lỗi) sẽ hiện ra.
Trong mục Installation Options (Các tuỳ chọn cài đặt), hãy đặt Deploy (Triển khai) thành Default APK (APK mặc định).
Nhấp vào Apply (Áp dụng) rồi nhấp vào OK.
Nhấp vào Run (Chạy) để cài đặt gói APK trên thiết bị kiểm thử của bạn.
Gỡ lỗi ứng dụng
Để gỡ lỗi ứng dụng, hãy nhấp vào nút Debug (Gỡ lỗi) trong Android Studio.
Để gỡ lỗi ứng dụng SDK, hãy chuyển đến Run > (Chạy >) Đính kèm vào quy trình, cho bạn thấy một cửa sổ bật lên
màn hình (như minh hoạ dưới đây). Đánh dấu vào hộp Show all processes (Hiển thị tất cả quy trình). Trong danh sách xuất hiện, hãy tìm quy trình có tên CLIENT_APP_PROCESS_sdk_sandbox
. Chọn tuỳ chọn này và thêm các điểm ngắt vào mã của ứng dụng SDK để bắt đầu gỡ lỗi SDK của bạn.
![The SDK app process appears in a list view near the bottom of the dialog (Quy trình ứng dụng SDK xuất hiện trong chế độ xem danh sách gần cuối hộp thoại)](https://developers.google.cn/static/privacy-sandbox/assets/images/android/debug-sdk-app.png?authuser=3&hl=vi)
Bắt đầu và dừng thời gian chạy SDK từ dòng lệnh
Để bắt đầu quy trình thời gian chạy SDK cho ứng dụng, hãy sử dụng lệnh shell sau:
adb shell cmd sdk_sandbox start [--user <USER_ID> | current] <CLIENT_APP_PACKAGE>
Tương tự như vậy, để dừng quy trình thời gian chạy SDK, hãy chạy lệnh sau:
adb shell cmd sdk_sandbox stop [--user <USER_ID> | current] <CLIENT_APP_PACKAGE>
Kiểm tra xem SDK nào đang được tải
Bạn có thể kiểm tra xem SDK nào đang được tải bằng cách sử dụng chức năng của getSandboxedSdks
bên trong SdkSandboxManager
.
Các điểm hạn chế
Để biết danh sách các tính năng đang tiến hành cho Thời gian chạy SDK, vui lòng xem ghi chú phát hành.
Mã mẫu
Chiến lược phát hành đĩa đơn Kho lưu trữ API Thời gian chạy SDK và API Bảo đảm quyền riêng tư trên GitHub chứa một tập hợp các dự án Android Studio riêng lẻ để giúp bạn tải bao gồm các mẫu minh hoạ cách khởi chạy và gọi SDK Thời gian chạy.Báo cáo lỗi và vấn đề
Phản hồi của bạn có vai trò quan trọng trong Hộp cát về quyền riêng tư trên Android! Hãy cho chúng tôi biết mọi vấn đề bạn tìm thấy hoặc ý tưởng để cải thiện Hộp cát về quyền riêng tư trên Android.
Đề xuất cho bạn
- Lưu ý: văn bản có đường liên kết sẽ hiện khi JavaScript tắt
- Thời gian chạy SDK
- Ghi chú phát hành
- Hướng dẫn cho nhà phát triển về Protected Audience API trên Android