Hướng dẫn cho nhà phát triển Thời gian chạy SDK

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.


Gửi ý kiến phản hồi

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ớ)

Chúng tôi sẽ khắc phục sự cố sau đây vào năm 2023:

  • API getAdIdgetAppSetId 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:

  1. Thiết lập môi trường phát triể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.

  2. 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 trình mô phỏng có chức năng hỗ trợ cho 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ị.

  1. 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.
  2. 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định cấu hình các dịch vụ quảng cáo cho API cụ thể.
  3. 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.
  4. 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.
  5. Xoá dependencies trong tệp build.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.
  6. 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ệp build.gradle trống. Nội dung của tệp này sẽ được điền sau trong hướng dẫn này.

  7. Thêm đoạn mã sau vào tệp gradle.properties:

    android.experimental.privacysandboxsdk.enable=true
    

  8. Tải hình ảnh trình mô phỏng Tiramisu (Tiện ích cấp độ 4) 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

  1. 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ục dependencies.

    plugins {
        id 'com.android.privacy-sandbox-sdk'
    }
    
    android {
        compileSdk 33
        compileSdkExtension 4
        minSdk 33
        targetSdk 33
        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>')
    }
    
  2. 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ộng SandboxedSdkProvider.

Đ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ượng SandboxedSdk mới. Hướng dẫn về dịch vụ ràng buộc giới thiệu nhiều cách để cung cấp IBinder. 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 doSomthing();
}
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.
        }
    }
}

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ụ:

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

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:

  1. Thêm quyền INTERNETACCESS_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"/>
    
  2. 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ượng SurfaceView để 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;
    
  3. Kiểm tra xem thiết bị có quy trình Thời gian chạy SDK hay không.

    1. 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.

    2. 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ủa SandboxedSdk.

      • Gọi loadSdk() từ nền trước. Nếu được gọi từ nền, hệ thống sẽ gửi SecurityException.

      • Kiểm tra OutcomeReceiver cho thực thể của SandboxedSdk để xác minh xem có xảy ra LoadSdkException 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ặc loadSdk 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ó.

  4. 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 {
    
          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 {
            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ện OutcomeReceiver<Bundle, RequestSurfacePackageException>:

    Kotlin

        private inner class RequestSurfacePackageOutcomeReceiverImpl :
                OutcomeReceiver {
            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 {
            @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()
    
  5. Trong onCreate(), hãy khởi chạy SdkSandboxManager, 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);
    }
    
  6. Để 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.

  7. 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:

  1. Mở dự án Android Studio cho ứng dụng khách của bạn.
  2. 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.
  3. 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).
  4. 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.
  5. Nhấp vào Apply (Áp dụng) rồi nhấp vào OK.
  6. 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.

  1. 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.

  2. Cài đặt APK trên thiết bị:

    adb install -t /path/to/your/standalone.apk
    
  3. 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.

  4. 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).

  5. Nhấp vào Apply (Áp dụng) rồi nhấp vào OK.

  6. 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 > Attach to Process (Chạy > Đính kèm quy trình). Thao tác này sẽ cho bạn thấy màn hình bật lên (hình 1). Đá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)
Hình 1. Màn hình Choose process (Chọn quy trình) nơi bạn chọn ứng dụng SDK để gỡ lỗi.

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>

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

Trang Kho lưu trữ API Thời gian chạy SDK và 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 bắt đầu, trong đó có các mẫu minh hoạ cách khởi động và gọi Thời gian chạy SDK.

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.