Özellikler ve API'ler

Android 17, geliştiriciler için harika yeni özellikler ve API'ler sunuyor. Aşağıdaki bölümlerde, ilgili API'leri kullanmaya başlamanıza yardımcı olmak için bu özellikler özetlenmiştir.

Yeni, değiştirilmiş ve kaldırılmış API'lerin ayrıntılı listesi için API farklılıkları raporunu inceleyin. Yeni API'lerle ilgili ayrıntılar için Android API referansını ziyaret edin. Yeni API'ler görünürlük için vurgulanmıştır.

Platform değişikliklerinin uygulamalarınızı etkileyebileceği alanları da incelemeniz gerekir. Daha fazla bilgi için aşağıdaki sayfalara göz atın:

Temel işlevler

Android 17, temel Android işlevleriyle ilgili aşağıdaki yeni özellikleri ekler.

Yeni ProfilingManager tetikleyicileri

Android 17, performans sorunlarında hata ayıklamak için ayrıntılı veriler toplamanıza yardımcı olmak amacıyla ProfilingManager'a çeşitli yeni sistem tetikleyicileri ekler.

Yeni tetikleyiciler:

  • TRIGGER_TYPE_COLD_START: Tetikleyici, uygulamanın baştan başlatılması sırasında gerçekleşir. Yanıt hem çağrı yığını örneği hem de sistem izi içerir.
  • TRIGGER_TYPE_OOM: Bir uygulama OutOfMemoryError oluşturduğunda ve buna yanıt olarak Java Heap Dump sağladığında tetikleyici gerçekleşir.
  • TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE: Tetikleyici, bir uygulama anormal ve aşırı CPU kullanımı nedeniyle kapatıldığında gerçekleşir ve yanıt olarak bir çağrı yığını örneği sağlar.
  • TRIGGER_TYPE_ANOMALY: Aşırı bağlayıcı çağrıları ve aşırı bellek kullanımı gibi sistem performansı anormalliklerini tespit edin.

Sistem tetikleyicisinin nasıl ayarlanacağını öğrenmek için tetikleyici tabanlı profilleme ve profilleme verilerini alma ve analiz etme ile ilgili dokümanlara bakın.

Uygulama anormallikleri için profilleme tetikleyicisi

Android 17, kaynak yoğun davranışları ve olası uyumluluk gerilemelerini izleyen cihaz üzerinde anormallik algılama hizmetini kullanıma sunar. ProfilingManager ile entegre olan bu hizmet, uygulamanızın sistem tarafından algılanan belirli etkinlikler tarafından tetiklenen profil oluşturma yapılarını almasına olanak tanır.

Aşırı bağlayıcı çağrıları ve aşırı bellek kullanımı gibi sistem performansı sorunlarını tespit etmek için TRIGGER_TYPE_ANOMALY tetikleyicisini kullanın. Bir uygulama, işletim sistemi tarafından tanımlanan bellek sınırlarını ihlal ettiğinde anomali tetikleyici, geliştiricilerin bellek sorunlarını belirleyip düzeltmesine yardımcı olmak için uygulamaya özel yığın dökümleri almasına olanak tanır. Ayrıca, aşırı bağlayıcı spam'i için anomali tetikleyici, bağlayıcı işlemlerinde yığın örnekleme profili sağlar.

Bu API geri çağırması, sistem tarafından uygulanan yaptırımlardan önce gerçekleşir. Örneğin, uygulamanın bellek sınırlarını aşması nedeniyle sistem tarafından sonlandırılmasından önce geliştiricilerin hata ayıklama verilerini toplamasına yardımcı olabilir.

val profilingManager =
    applicationContext.getSystemService(ProfilingManager::class.java)
val triggers = ArrayList<ProfilingTrigger>()
triggers.add(ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_ANOMALY))
val mainExecutor: Executor = Executors.newSingleThreadExecutor()
val resultCallback = Consumer<ProfilingResult> { profilingResult ->
    if (profilingResult.errorCode != ProfilingResult.ERROR_NONE) {
        // upload profile result to server for further analysis
        setupProfileUploadWorker(profilingResult.resultFilePath)
    }
    profilingManager.registerForAllProfilingResults(mainExecutor,
                                                    resultCallback)
    profilingManager.addProfilingTriggers(triggers)
}

JobDebugInfo API'leri

Android 17 引入了新的 JobDebugInfo API,可帮助开发者调试其 JobScheduler 作业,了解作业未运行的原因、运行时长以及其他汇总信息。

扩展后的 JobDebugInfo API 的第一个方法是 getPendingJobReasonStats(),该方法会返回一个映射,其中包含作业处于待执行状态的原因及其各自的累计待执行时长。此方法将 getPendingJobReasonsHistory()getPendingJobReasons() 方法联接在一起,可让您了解预定作业未按预期运行的原因,但通过在单个方法中同时提供时长和作业原因,简化了信息检索。

例如,对于指定的 jobId,该方法可能会返回 PENDING_JOB_REASON_CONSTRAINT_CHARGING 和 60000 毫秒的时长,表示作业因未满足充电约束而处于等待状态 60000 毫秒。

Boşta kalma sırasında izin verilen alarmlar için dinleyici desteğiyle uyandırma kilitlerini azaltma

Android 17, PendingIntent yerine OnAlarmListener kabul eden yeni bir AlarmManager.setExactAndAllowWhileIdle varyantını kullanıma sunar. Bu yeni geri çağırmaya dayalı mekanizma, şu anda soket bağlantılarını koruyan mesajlaşma uygulamaları gibi periyodik görevleri gerçekleştirmek için sürekli uyandırma kilitlerine dayanan uygulamalar için idealdir.

Gizlilik

Android 17, kullanıcı gizliliğini iyileştirmek için aşağıdaki yeni özellikleri içerir.

Encrypted Client Hello (ECH) platform desteği

Android 17, ağ iletişimlerinde önemli bir gizlilik iyileştirmesi olan Encrypted Client Hello (ECH) için platform desteği sunuyor. ECH, ilk TLS el sıkışması sırasında Sunucu Adı Göstergesi'ni (SNI) şifreleyen bir TLS 1.3 uzantısıdır. Bu şifreleme, bir uygulamanın bağlandığı alanın ağ aracıları tarafından tanımlanmasını zorlaştırarak kullanıcı gizliliğinin korunmasına yardımcı olur.

Platform artık ağ kitaplıklarının ECH'yi uygulaması için gerekli API'leri içeriyor. Buna, ECH yapılandırmaları içeren HTTPS DNS kayıtlarını sorgulamak için DnsResolver'daki yeni özellikler ve bir alana bağlanırken bu yapılandırmaları ileterek ECH'yi etkinleştirmek için Conscrypt'in SSLEngines ve SSLSockets'indeki yeni yöntemler dahildir. Geliştiriciler, ECH tercihlerini (ör. fırsatçı bir şekilde etkinleştirme veya kullanımını zorunlu kılma) yeni <domainEncryption> öğesi aracılığıyla yapılandırabilir. Bu öğe, Ağ Güvenliği Yapılandırma dosyası içinde yer alır ve global olarak veya alan bazında geçerlidir.

HttpEngine, WebView ve OkHttp gibi popüler ağ kitaplıklarının gelecekteki güncellemelerde bu platform API'lerini entegre etmesi bekleniyor. Bu sayede uygulamaların ECH'yi benimsemesi ve kullanıcı gizliliğini artırması kolaylaşacak.

Daha fazla bilgi için Şifrelenmiş İstemci Merhaba belgesine bakın.

Android kişi seçici

Android Kişi Seçici, kullanıcıların kişilerle uygulamanız arasında paylaşım yapması için standartlaştırılmış ve göz atılabilir bir arayüzdür. Android 17 (API düzeyi 37) veya sonraki sürümlerin yüklü olduğu cihazlarda kullanılabilen seçici, geniş kapsamlı READ_CONTACTS iznine gizliliği korumaya yönelik bir alternatif sunar. Uygulamanız, kullanıcının tüm adres defterine erişim isteğinde bulunmak yerine ihtiyaç duyduğu veri alanlarını (ör. telefon numaraları veya e-posta adresleri) belirtir ve kullanıcı, paylaşılacak belirli kişileri seçer. Bu sayede uygulamanız yalnızca seçilen verilere okuma erişimi kazanır. Böylece, kullanıcı arayüzünü oluşturmaya veya bakımını yapmaya gerek kalmadan yerleşik arama, profil değiştirme ve çoklu seçim özellikleriyle tutarlı bir kullanıcı deneyimi sunarken ayrıntılı kontrol sağlanır.

Daha fazla bilgi için Kişi seçici dokümanlarına bakın.

Güvenlik

Android 17, cihaz ve uygulama güvenliğini artırmak için aşağıdaki yeni özellikleri ekler.

Android Gelişmiş Koruma Modu (AAPM)

Android Gelişmiş Koruma Modu, Android kullanıcılarına güçlü bir yeni güvenlik özellikleri paketi sunarak kullanıcıları, özellikle de daha yüksek risk altındaki kullanıcıları gelişmiş saldırılardan koruma konusunda önemli bir adım atıyor. İsteğe bağlı bir özellik olarak tasarlanan AAPM, kullanıcıların istediği zaman etkinleştirerek güvenlik korumaları uygulayabileceği tek bir yapılandırma ayarıyla etkinleştirilir.

Bu temel yapılandırmalar arasında bilinmeyen kaynaklardan uygulama yüklenmesini engelleme (yan yükleme), USB veri sinyalini kısıtlama ve Google Play Protect taramasını zorunlu kılma yer alır. Bu yapılandırmalar, cihazın saldırı yüzey alanını önemli ölçüde azaltır. Geliştiriciler, modun durumunu algılamak için AdvancedProtectionManager API'sini kullanarak bu özellikle entegrasyon yapabilir. Böylece, kullanıcı modu etkinleştirdiğinde uygulamalar otomatik olarak daha sıkı bir güvenlik duruşu benimseyebilir veya yüksek riskli işlevleri kısıtlayabilir.

PQC APK İmzalama

Android 现在支持混合 APK 签名方案,以保护应用的签名身份免受利用量子计算的攻击的潜在威胁。此功能引入了一种新的 APK 签名方案,可让您将经典签名密钥(例如 RSA 或 EC)与新的后量子加密 (PQC) 算法 (ML-DSA) 配对。

这种混合方法可确保您的应用在未来免受量子攻击,同时与依赖于经典签名验证的旧版 Android 和设备保持完全的向后兼容性。

对开发者的影响

  • 使用 Play 应用签名的应用:如果您使用 Play 应用签名,可以等待 Google Play 为您提供使用 Google Play 生成的 PQC 密钥升级混合签名的选项,从而确保您的应用受到保护,而无需手动管理密钥。
  • 使用自行管理的密钥的应用:自行管理签名密钥的开发者可以利用更新后的 Android build 工具(例如 apksigner)轮换到混合身份,将 PQC 密钥与新的经典密钥相结合。(您必须创建新的经典密钥,无法重复使用旧密钥。)

Bağlantı

Android 17, cihaz ve uygulama bağlantısını iyileştirmek için aşağıdaki özellikleri ekler.

Kısıtlanmış uydu ağları

实现优化,使应用能够在低带宽卫星网络上有效运行。

Kullanıcı deneyimi ve sistem arayüzü

Android 17, kullanıcı deneyimini iyileştirmek için aşağıdaki değişiklikleri içerir.

Özel Asistan ses akışı

Android 17 为 Google 助理应用引入了专用的 Google 助理音量流, 以便使用 USAGE_ASSISTANT 进行播放。此项更改将 Google 助理音频与标准媒体流分离,让用户可以单独控制这两个音量。这样便可实现以下场景:将媒体播放静音,同时保持 Google 助理响应的可听性,反之亦然。

有权访问新的 MODE_ASSISTANT_CONVERSATION 音频模式的 Google 助理应用可以进一步提高音量控制的一致性。Google 助理应用可以使用此模式向系统提供有关活跃 Google 助理会话的提示,确保可以在活跃 USAGE_ASSISTANT 播放之外或使用连接的蓝牙外设控制 Google 助理流。

Handoff

Handoff, Android 17'de kullanıma sunulacak yeni bir özellik ve API'dir. Uygulama geliştiriciler, kullanıcılarına cihazlar arası süreklilik sağlamak için bu özelliği entegre edebilir. Kullanıcının bir Android cihazda uygulama etkinliği başlatmasına ve bunu başka bir Android cihaza aktarmasına olanak tanır. Devretme, kullanıcının cihazının arka planında çalışır ve kullanıcının yakındaki diğer cihazlarındaki kullanılabilir etkinlikleri, alıcı cihazdaki başlatıcı ve görev çubuğu gibi çeşitli giriş noktaları üzerinden gösterir.

Uygulamalar, Handoff'u kullanarak aynı yerel Android uygulamasını başlatabilir. Bunun için uygulamanın, alıcı cihaza yüklenmiş ve kullanılabilir olması gerekir. Bu uygulama içi akışta, kullanıcı belirlenen etkinliğe derin bağlantıyla yönlendirilir. Alternatif olarak, uygulamadan web'e geçiş, yedek seçenek olarak sunulabilir veya doğrudan URL ile geçiş ile uygulanabilir.

Aktarma desteği, etkinlik bazında uygulanır. Devretme'yi etkinleştirmek için etkinliğin setHandoffEnabled() yöntemini çağırın. Devretme işlemiyle birlikte ek verilerin de aktarılması gerekebilir. Böylece, alıcı cihazda yeniden oluşturulan etkinlik uygun durumu geri yükleyebilir. Devretme özelliğinin, etkinliği alıcı cihazda nasıl işleyip yeniden oluşturacağını belirten ayrıntıları içeren bir HandoffActivityData nesnesi döndürmek için onHandoffActivityDataRequested() geri çağırma işlevini uygulayın.

Canlı güncelleme - Anlamsal renk API'si

Android 17 ile Canlı Güncelleme, evrensel anlamı olan renkleri desteklemek için Semantik Renklendirme API'lerini kullanıma sunuyor.

Aşağıdaki sınıflar semantik renklendirmeyi destekler:

Boyama

  • Yeşil: Güvenlikle ilişkilidir. Bu renk, güvende olduğunuzu bildirmek için kullanılmalıdır.
  • Turuncu: Dikkat çekmek ve fiziksel tehlikeleri işaretlemek için kullanılır. Bu renk, kullanıcıların daha iyi koruma ayarları yapmak için dikkat etmesi gereken durumlarda kullanılmalıdır.
  • Kırmızı: Genellikle tehlikeyi veya durmayı gösterir. Kullanıcıların dikkatini acilen çekmeniz gereken durumlarda gösterilmelidir.
  • Mavi: Bilgilendirici olan ve diğer içeriklerden ayrılması gereken içerikler için nötr renk.

Aşağıdaki örnekte, bir bildirimdeki metne anlamsal stillerin nasıl uygulanacağı gösterilmektedir:

  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)

Android 17 için UWB Downlink-TDoA API'si

借助下行链路到达时间差 (DL-TDoA) 测距功能,设备可以通过测量信号的相对到达时间来确定其相对于多个锚点的相对位置。

以下代码段演示了如何初始化 测距管理器、验证设备功能以及启动 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.RangingCapabilitiesCallback {
            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 = byteArrayOf(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.RangingCapabilitiesCallback() {
            @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());
        byte[] rangingRoundIndexes = new byte[] {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
        }
    }
}

带外 (OOB) 配置

以下代码段提供了 Wi-Fi 和 BLE 的 DL-TDoA OOB 配置数据示例:

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
};

如果您无法使用 OOB 配置(因为缺少该配置),或者需要更改 OOB 配置中没有的默认值,则可以使用 DlTdoaRangingParams.Builder 构建参数,如以下代码段所示。您可以使用这些参数来代替 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();