Tính năng và API

Android 17 mang đến cho nhà phát triển các tính năng và API mới tuyệt vời. Các phần sau đây tóm tắt những tính năng này để giúp bạn làm quen với các API liên quan.

Để biết danh sách chi tiết về các API mới, đã được sửa đổi và đã bị xoá, hãy đọc báo cáo điểm khác biệt về API. Để biết thông tin chi tiết về các API mới, hãy truy cập vào tài liệu tham khảo API cho Android (các API mới được trình bày nổi bật).

Bạn cũng nên xem xét những khía cạnh mà các thay đổi của nền tảng có thể ảnh hưởng đến ứng dụng của bạn. Để biết thêm thông tin, hãy xem các trang sau:

Chức năng cốt lõi

Android 17 bổ sung các tính năng mới sau đây liên quan đến chức năng cốt lõi của Android.

Điều kiện kích hoạt ProfilingManager mới

Android 17 bổ sung một số điều kiện kích hoạt hệ thống mới cho ProfilingManager để giúp bạn thu thập dữ liệu chuyên sâu nhằm gỡ lỗi hiệu suất.

Các điều kiện kích hoạt mới là:

  • TRIGGER_TYPE_COLD_START: Sự kiện kích hoạt xảy ra trong quá trình khởi động nguội ứng dụng. Thao tác này cung cấp cả mẫu ngăn xếp lệnh gọi và dấu vết hệ thống trong phản hồi.
  • TRIGGER_TYPE_OOM: Sự kiện kích hoạt xảy ra khi một ứng dụng gửi OutOfMemoryError và cung cấp một Java Heap Dump để phản hồi.
  • TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE: Sự kiện kích hoạt xảy ra khi một ứng dụng bị tắt do mức sử dụng CPU bất thường và quá mức, đồng thời cung cấp một mẫu ngăn xếp lệnh gọi để phản hồi.

Để tìm hiểu cách thiết lập trình kích hoạt hệ thống, hãy xem tài liệu về phân tích hiệu suất dựa trên trình kích hoạt và cách truy xuất và phân tích dữ liệu phân tích hiệu suất.

API JobDebugInfo

Android 17 ra mắt các API JobDebugInfo mới để giúp nhà phát triển gỡ lỗi các công việc JobScheduler của họ – lý do công việc không chạy, thời gian chạy và các thông tin tổng hợp khác.

Phương thức đầu tiên của JobDebugInfo API mở rộng là getPendingJobReasonStats(), phương thức này trả về một bản đồ gồm các lý do khiến công việc ở trạng thái đang chờ thực thi và thời lượng tích luỹ tương ứng của trạng thái đang chờ. Phương thức này kết hợp các phương thức getPendingJobReasonsHistory()getPendingJobReasons() để giúp bạn hiểu rõ lý do một công việc đã lên lịch không chạy như mong đợi, nhưng đơn giản hoá việc truy xuất thông tin bằng cách cung cấp cả thời lượng và lý do của công việc trong một phương thức duy nhất.

Ví dụ: đối với một jobId cụ thể, phương thức này có thể trả về PENDING_JOB_REASON_CONSTRAINT_CHARGING và thời lượng là 60000 mili giây, cho biết công việc đang ở trạng thái chờ trong 60000 mili giây do không đáp ứng được hạn chế về việc sạc.

Giảm số lượng khoá đánh thức bằng tính năng hỗ trợ trình nghe cho các báo thức cho phép ở trạng thái rảnh

Android 17 giới thiệu một biến thể mới của AlarmManager.setExactAndAllowWhileIdle mà chấp nhận một OnAlarmListener thay vì một PendingIntent. Cơ chế mới dựa trên lệnh gọi lại này rất phù hợp với những ứng dụng hiện dựa vào khoá đánh thức liên tục để thực hiện các tác vụ định kỳ, chẳng hạn như các ứng dụng nhắn tin duy trì kết nối socket.

Quyền riêng tư

Android 17 có các tính năng mới sau đây để cải thiện quyền riêng tư của người dùng.

Trình chọn người liên hệ trên Android

Bộ chọn Địa chỉ liên hệ của Android là một giao diện tiêu chuẩn mà người dùng có thể duyệt để chia sẻ danh bạ với ứng dụng của bạn. Bộ chọn này có trên các thiết bị chạy Android 17 (API cấp 37) trở lên và là một giải pháp thay thế giúp bảo vệ quyền riêng tư cho quyền READ_CONTACTS trên diện rộng. Thay vì yêu cầu quyền truy cập vào toàn bộ sổ địa chỉ của người dùng, ứng dụng của bạn sẽ chỉ định các trường dữ liệu mà ứng dụng cần, chẳng hạn như số điện thoại hoặc địa chỉ email, và người dùng sẽ chọn những người liên hệ cụ thể để chia sẻ. Điều này cấp cho ứng dụng của bạn quyền chỉ đọc đối với dữ liệu đã chọn, đảm bảo quyền kiểm soát chi tiết trong khi mang đến trải nghiệm người dùng nhất quán với các chức năng tìm kiếm, chuyển đổi hồ sơ và chọn nhiều mục được tích hợp sẵn mà không cần phải tạo hoặc duy trì giao diện người dùng.

Để biết thêm thông tin, hãy xem tài liệu về công cụ chọn người liên hệ.

Bảo mật

Android 17 bổ sung các tính năng mới sau đây để cải thiện tính bảo mật của thiết bị và ứng dụng.

Chế độ Bảo vệ nâng cao trên Android (AAPM)

Chế độ Bảo vệ nâng cao của Android cung cấp cho người dùng Android một bộ tính năng bảo mật mới mạnh mẽ, đánh dấu một bước tiến quan trọng trong việc bảo vệ người dùng (đặc biệt là những người dùng có nguy cơ cao hơn) khỏi các cuộc tấn công tinh vi. Được thiết kế như một tính năng chọn sử dụng, AAPM được kích hoạt bằng một chế độ cài đặt cấu hình duy nhất mà người dùng có thể bật bất cứ lúc nào để áp dụng một bộ biện pháp bảo vệ bảo mật theo ý kiến riêng.

Các cấu hình cốt lõi này bao gồm việc chặn cài đặt ứng dụng từ các nguồn không xác định (tải ứng dụng bên ngoài), hạn chế tín hiệu dữ liệu USB và bắt buộc quét bằng Google Play Protect, giúp giảm đáng kể phạm vi tấn công của thiết bị. Nhà phát triển có thể tích hợp với tính năng này bằng cách sử dụng API AdvancedProtectionManager để phát hiện trạng thái của chế độ, cho phép các ứng dụng tự động áp dụng trạng thái bảo mật tăng cường hoặc hạn chế chức năng có rủi ro cao khi người dùng đã chọn sử dụng.

Ký APK bằng PQC

Giờ đây, Android hỗ trợ một lược đồ chữ ký APK kết hợp để bảo vệ danh tính ký của ứng dụng trước mối đe doạ tiềm ẩn của các cuộc tấn công sử dụng điện toán lượng tử. Tính năng này giới thiệu một Lược đồ chữ ký APK mới, cho phép bạn ghép nối khoá ký cổ điển (chẳng hạn như RSA hoặc EC) với một thuật toán mật mã học hậu lượng tử (PQC) mới (ML-DSA).

Phương pháp kết hợp này đảm bảo ứng dụng của bạn vẫn an toàn trước các cuộc tấn công lượng tử trong tương lai, đồng thời duy trì khả năng tương thích ngược hoàn toàn với các phiên bản Android cũ và những thiết bị dựa vào quy trình xác minh chữ ký cổ điển.

Ảnh hưởng đến nhà phát triển

  • Ứng dụng sử dụng Tính năng ký ứng dụng của Play: Nếu sử dụng Tính năng ký ứng dụng của Play, bạn có thể đợi Google Play cung cấp cho bạn lựa chọn nâng cấp chữ ký kết hợp bằng khoá PQC do Google Play tạo. Nhờ đó, ứng dụng của bạn sẽ được bảo vệ mà không cần quản lý khoá theo cách thủ công.
  • Ứng dụng sử dụng khoá tự quản lý: Nhà phát triển tự quản lý khoá ký có thể sử dụng các công cụ tạo bản dựng Android mới (chẳng hạn như apksigner) để chuyển sang một danh tính kết hợp, kết hợp khoá PQC với một khoá cổ điển mới. (Bạn phải tạo một khoá cổ điển mới, bạn không thể sử dụng lại khoá cũ.)

Khả năng kết nối

Android 17 bổ sung các tính năng sau để cải thiện khả năng kết nối của thiết bị và ứng dụng.

Mạng vệ tinh bị hạn chế

Triển khai các hoạt động tối ưu hoá để cho phép ứng dụng hoạt động hiệu quả trên các mạng vệ tinh có băng thông thấp.

Trải nghiệm người dùng và giao diện người dùng hệ thống

Android 17 có những thay đổi sau đây để cải thiện trải nghiệm người dùng.

Luồng âm lượng riêng cho Trợ lý

Android 17 giới thiệu một luồng âm lượng riêng cho Trợ lý đối với các ứng dụng trợ lý, để phát bằng USAGE_ASSISTANT. Thay đổi này tách âm thanh của Trợ lý khỏi luồng nội dung nghe nhìn tiêu chuẩn, giúp người dùng có thể kiểm soát riêng cả hai âm lượng. Điều này cho phép các trường hợp như tắt tiếng phát nội dung nghe nhìn trong khi vẫn duy trì khả năng nghe được các câu trả lời của Trợ lý và ngược lại.

Các ứng dụng Trợ lý có quyền truy cập vào chế độ âm thanh MODE_ASSISTANT_CONVERSATION mới có thể cải thiện hơn nữa tính nhất quán của việc kiểm soát âm lượng. Các ứng dụng Trợ lý có thể sử dụng chế độ này để cung cấp gợi ý cho hệ thống về một phiên Trợ lý đang hoạt động, đảm bảo rằng luồng Trợ lý có thể được kiểm soát bên ngoài quá trình phát USAGE_ASSISTANT đang hoạt động hoặc bằng các thiết bị ngoại vi Bluetooth được kết nối.

Handoff

Handoff là một tính năng và API mới sắp có trên Android 17. Các nhà phát triển ứng dụng có thể tích hợp tính năng này để mang đến trải nghiệm liền mạch trên nhiều thiết bị cho người dùng. Tính năng này cho phép người dùng bắt đầu một hoạt động của ứng dụng trên một thiết bị Android và chuyển hoạt động đó sang một thiết bị Android khác. Tính năng Handoff chạy ở chế độ nền trên thiết bị của người dùng và hiển thị các hoạt động có sẵn từ các thiết bị khác ở gần của người dùng thông qua nhiều điểm truy cập, chẳng hạn như trình chạy và thanh tác vụ, trên thiết bị nhận.

Các ứng dụng có thể chỉ định tính năng Bàn giao để khởi chạy cùng một ứng dụng Android gốc, nếu ứng dụng đó được cài đặt và có trên thiết bị nhận. Trong quy trình từ ứng dụng đến ứng dụng này, người dùng được liên kết sâu đến hoạt động được chỉ định. Ngoài ra, bạn có thể cung cấp tính năng Bàn giao từ ứng dụng sang web dưới dạng lựa chọn dự phòng hoặc triển khai trực tiếp bằng tính năng Bàn giao URL.

Tính năng handoff được triển khai dựa trên từng hoạt động. Để bật tính năng Handoff, hãy gọi phương thức setHandoffEnabled() cho hoạt động. Bạn có thể cần chuyển thêm dữ liệu cùng với quá trình chuyển giao để hoạt động được tạo lại trên thiết bị nhận có thể khôi phục trạng thái thích hợp. Triển khai lệnh gọi lại onHandoffActivityRequested() để trả về một đối tượng HandoffActivityData chứa thông tin chi tiết chỉ định cách Handoff nên xử lý và tạo lại hoạt động trên thiết bị nhận.

Thông tin cập nhật trực tiếp – API màu sắc theo ngữ nghĩa

Với Android 17, Cập nhật trực tiếp sẽ ra mắt API Tô màu theo ngữ nghĩa để hỗ trợ các màu có ý nghĩa phổ quát.

Các lớp sau đây hỗ trợ tính năng tô màu theo ngữ nghĩa:

Tô màu

  • Màu xanh lục: Liên quan đến sự an toàn. Bạn nên dùng màu này trong trường hợp muốn cho mọi người biết rằng bạn đang ở trong tình huống an toàn.
  • Màu cam: Dùng để chỉ sự thận trọng và đánh dấu các mối nguy hiểm vật lý. Bạn nên dùng màu này trong trường hợp người dùng cần chú ý để đặt chế độ bảo vệ tốt hơn.
  • Màu đỏ: Thường biểu thị sự nguy hiểm, dừng lại. Bạn nên dùng màu này trong trường hợp cần thu hút sự chú ý của mọi người một cách khẩn cấp.
  • Màu xanh dương: Màu trung tính cho nội dung mang tính thông tin và cần nổi bật so với nội dung khác.

Ví dụ sau đây cho biết cách áp dụng kiểu theo ngữ nghĩa cho văn bản trong một thông báo:

  val ssb = SpannableStringBuilder()
        .append("Colors: ")
        .append("NONE", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_UNSPECIFIED), 0)
        .append(", ")
        .append("INFO", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_INFO), 0)
        .append(", ")
        .append("SAFE", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_SAFE), 0)
        .append(", ")
        .append("CAUTION", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_CAUTION), 0)
        .append(", ")
        .append("DANGER", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_DANGER), 0)

    Notification.Builder(context, channelId)
          .setSmallIcon(R.drawable.ic_icon)
          .setContentTitle("Hello World!")
          .setContentText(ssb)
          .setOngoing(true)
              .setRequestPromotedOngoing(true)

UWB Downlink-TDoA API cho Android 17

Phạm vi Chênh lệch thời gian đến của đường xuống (DL-TDoA) cho phép thiết bị xác định vị trí của thiết bị so với nhiều điểm truy cập bằng cách đo thời gian đến tương đối của các tín hiệu.

Đoạn mã sau đây minh hoạ cách khởi tạo Trình quản lý đo khoảng cách, xác minh các chức năng của thiết bị và bắt đầu một phiên DL-TDoA:

Kotlin

class RangingApp {

    fun initDlTdoa(context: Context) {
        // Initialize the Ranging Manager
        val rangingManager = context.getSystemService(RangingManager::class.java)

        // Register for device capabilities
        val capabilitiesCallback = object : RangingManager.CapabilitiesCallback {
            override fun onRangingCapabilities(capabilities: RangingCapabilities) {
                // Make sure Dl-TDoA is supported before starting the session
                if (capabilities.uwbCapabilities != null && capabilities.uwbCapabilities!!.isDlTdoaSupported) {
                    startDlTDoASession(context)
                }
            }
        }
        rangingManager.registerCapabilitiesCallback(Executors.newSingleThreadExecutor(), capabilitiesCallback)
    }

    fun startDlTDoASession(context: Context) {

        // Initialize the Ranging Manager
        val rangingManager = context.getSystemService(RangingManager::class.java)

        // Create session and configure parameters
        val executor = Executors.newSingleThreadExecutor()
        val rangingSession = rangingManager.createRangingSession(executor, RangingSessionCallback())
        val rangingRoundIndexes = intArrayOf(0)
        val config: ByteArray = byteArrayOf() // OOB config data
        val params = DlTdoaRangingParams.createFromFiraConfigPacket(config, rangingRoundIndexes)

        val rangingDevice = RangingDevice.Builder().build()
        val rawTagDevice = RawRangingDevice.Builder()
            .setRangingDevice(rangingDevice)
            .setDlTdoaRangingParams(params)
            .build()

        val dtTagConfig = RawDtTagRangingConfig.Builder(rawTagDevice).build()

        val preference = RangingPreference.Builder(DEVICE_ROLE_DT_TAG, dtTagConfig)
            .setSessionConfig(SessionConfig.Builder().build())
            .build()

        // Start the ranging session
        rangingSession.start(preference)
    }
}

private class RangingSessionCallback : RangingSession.Callback {
    override fun onDlTdoaResults(peer: RangingDevice, measurement: DlTdoaMeasurement) {
        // Process measurement results here
    }
}

Java

public class RangingApp {

    public void initDlTdoa(Context context) {

        // Initialize the Ranging Manager
        RangingManager rangingManager = context.getSystemService(RangingManager.class);

        // Register for device capabilities
        RangingManager.CapabilitiesCallback capabilitiesCallback = new RangingManager.CapabilitiesCallback() {
            @Override
            public void onRangingCapabilities(RangingCapabilities capabilities) {
                // Make sure Dl-TDoA is supported before starting the session
                if (capabilities.getUwbCapabilities() != null && capabilities.getUwbCapabilities().isDlTdoaSupported) {
                    startDlTDoASession(context);
                }
            }
        };
        rangingManager.registerCapabilitiesCallback(Executors.newSingleThreadExecutor(), capabilitiesCallback);
    }

    public void startDlTDoASession(Context context) {
        RangingManager rangingManager = context.getSystemService(RangingManager.class);

        // Create session and configure parameters
        Executor executor = Executors.newSingleThreadExecutor();
        RangingSession rangingSession = rangingManager.createRangingSession(executor, new RangingSessionCallback());
        int[] rangingRoundIndexes = new int[] {0};
        byte[] config = new byte[0]; // OOB config data
        DlTdoaRangingParams params = DlTdoaRangingParams.createFromFiraConfigPacket(config, rangingRoundIndexes);

        RangingDevice rangingDevice = new RangingDevice.Builder().build();
        RawRangingDevice rawTagDevice = new RawRangingDevice.Builder()
                .setRangingDevice(rangingDevice)
                .setDlTdoaRangingParams(params)
                .build();

        RawDtTagRangingConfig dtTagConfig = new RawDtTagRangingConfig.Builder(rawTagDevice).build();

        RangingPreference preference = new RangingPreference.Builder(DEVICE_ROLE_DT_TAG, dtTagConfig)
                .setSessionConfig(new SessionConfig.Builder().build())
                .build();

        // Start the ranging session
        rangingSession.start(preference);
    }

    private static class RangingSessionCallback implements RangingSession.Callback {

        @Override
        public void onDlTdoaResults(RangingDevice peer, DlTdoaMeasurement measurement) {
            // Process measurement results here
        }
    }
}

Cấu hình ngoài băng tần (OOB)

Đoạn mã sau đây cung cấp ví dụ về dữ liệu cấu hình DL-TDoA OOB cho Wi-Fi và BLE:

Java

// Wifi Configuration
byte[] wifiConfig = {
    (byte) 0xDD, (byte) 0x2D, (byte) 0x5A, (byte) 0x18, (byte) 0xFF, // Header
    (byte) 0x5F, (byte) 0x19, // FiRa Sub-Element
    (byte) 0x02, (byte) 0x00, // Profile ID
    (byte) 0x06, (byte) 0x02, (byte) 0x20, (byte) 0x08, // MAC Address
    (byte) 0x14, (byte) 0x01, (byte) 0x0C, // Preamble Index
    (byte) 0x27, (byte) 0x02, (byte) 0x08, (byte) 0x07, // Vendor ID
    (byte) 0x28, (byte) 0x06, (byte) 0xCA, (byte) 0xC8, (byte) 0xA6, (byte) 0xF7, (byte) 0x6F, (byte) 0x08, // Static STS IV
    (byte) 0x08, (byte) 0x02, (byte) 0x60, (byte) 0x09, // Slot Duration
    (byte) 0x1B, (byte) 0x01, (byte) 0x0A, // Slots per RR
    (byte) 0x09, (byte) 0x04, (byte) 0xE8, (byte) 0x03, (byte) 0x00, (byte) 0x00, // Duration
    (byte) 0x9F, (byte) 0x04, (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01  // Session ID
};

// BLE Configuration
byte[] bleConfig = {
    (byte) 0x2D, (byte) 0x16, (byte) 0xF4, (byte) 0xFF, // Header
    (byte) 0x5F, (byte) 0x19, // FiRa Sub-Element
    (byte) 0x02, (byte) 0x00, // Profile ID
    (byte) 0x06, (byte) 0x02, (byte) 0x20, (byte) 0x08, // MAC Address
    (byte) 0x14, (byte) 0x01, (byte) 0x0C, // Preamble Index
    (byte) 0x27, (byte) 0x02, (byte) 0x08, (byte) 0x07, // Vendor ID
    (byte) 0x28, (byte) 0x06, (byte) 0xCA, (byte) 0xC8, (byte) 0xA6, (byte) 0xF7, (byte) 0x6F, (byte) 0x08, // Static STS IV
    (byte) 0x08, (byte) 0x02, (byte) 0x60, (byte) 0x09, // Slot Duration
    (byte) 0x1B, (byte) 0x01, (byte) 0x0A, // Slots per RR
    (byte) 0x09, (byte) 0x04, (byte) 0xE8, (byte) 0x03, (byte) 0x00, (byte) 0x00, // Duration
    (byte) 0x9F, (byte) 0x04, (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01  // Session ID
};

Nếu không dùng được cấu hình OOB vì cấu hình này bị thiếu hoặc nếu cần thay đổi các giá trị mặc định không có trong cấu hình OOB, bạn có thể tạo các tham số bằng DlTdoaRangingParams.Builder như trong đoạn mã sau. Bạn có thể sử dụng các tham số này thay cho DlTdoaRangingParams.createFromFiraConfigPacket():

Kotlin

val dlTdoaParams = DlTdoaRangingParams.Builder(1)
    .setComplexChannel(UwbComplexChannel.Builder()
            .setChannel(9).setPreambleIndex(10).build())
    .setDeviceAddress(deviceAddress)
    .setSessionKeyInfo(byteArrayOf(0x01, 0x02, 0x03, 0x04))
    .setRangingIntervalMillis(240)
    .setSlotDuration(UwbRangingParams.DURATION_2_MS)
    .setSlotsPerRangingRound(20)
    .setRangingRoundIndexes(byteArrayOf(0x01, 0x05))
    .build()

Java

DlTdoaRangingParams dlTdoaParams = new DlTdoaRangingParams.Builder(1)
    .setComplexChannel(new UwbComplexChannel.Builder()
            .setChannel(9).setPreambleIndex(10).build())
    .setDeviceAddress(deviceAddress)
    .setSessionKeyInfo(new byte[]{0x01, 0x02, 0x03, 0x04})
    .setRangingIntervalMillis(240)
    .setSlotDuration(UwbRangingParams.DURATION_2_MS)
    .setSlotsPerRangingRound(20)
    .setRangingRoundIndexes(new byte[]{0x01, 0x05})
    .build();