การเปลี่ยนแปลงลักษณะการทำงาน: แอปที่กำหนดเป้าหมายเป็น Android 14 ขึ้นไป

Android 14 มีการเปลี่ยนแปลงลักษณะการทำงานที่อาจส่งผลต่อแอปของคุณเช่นเดียวกับรุ่นก่อนหน้า การเปลี่ยนแปลงลักษณะการทำงานต่อไปนี้จะมีผลเฉพาะกับแอปที่กำหนดเป้าหมายเป็น Android 14 (API ระดับ 34) ขึ้นไป หากแอปกำหนดเป้าหมายเป็น Android 14 ขึ้นไป คุณควรแก้ไขแอปให้รองรับลักษณะการทำงานเหล่านี้อย่างเหมาะสม ในกรณีที่เกี่ยวข้อง

นอกจากนี้ โปรดตรวจสอบรายการการเปลี่ยนแปลงลักษณะการทำงานที่มีผลกับแอปทั้งหมด ที่ทำงานบน Android 14 ไม่ว่าtargetSdkVersionของแอปจะเป็นอย่างไร

ฟังก์ชันหลัก

ต้องระบุประเภทบริการที่ทำงานอยู่เบื้องหน้า

If your app targets Android 14 (API level 34) or higher, it must specify at least one foreground service type for each foreground service within your app. You should choose a foreground service type that represents your app's use case. The system expects foreground services that have a particular type to satisfy a particular use case.

If a use case in your app isn't associated with any of these types, it's strongly recommended that you migrate your logic to use WorkManager or user-initiated data transfer jobs.

การบังคับใช้สิทธิ์ BLUETOOTH_CONNECT ใน BluetoothAdapter

Android 14 จะบังคับใช้สิทธิ์ BLUETOOTH_CONNECT เมื่อเรียกใช้เมธอด BluetoothAdapter getProfileConnectionState() สำหรับแอปที่กำหนดเป้าหมายเป็น Android 14 (API ระดับ 34) ขึ้นไป

วิธีนี้ต้องใช้สิทธิ์ BLUETOOTH_CONNECT อยู่แล้ว แต่ยังไม่ได้บังคับใช้ ตรวจสอบว่าแอปได้ประกาศ BLUETOOTH_CONNECT ในไฟล์ AndroidManifest.xml ของแอปดังที่แสดงในข้อมูลโค้ดต่อไปนี้และตรวจสอบว่าผู้ใช้ให้สิทธิ์แล้วก่อนที่จะเรียกใช้ getProfileConnectionState

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

การอัปเดต OpenJDK 17

Android 14 continues the work of refreshing Android's core libraries to align with the features in the latest OpenJDK LTS releases, including both library updates and Java 17 language support for app and platform developers.

A few of these changes can affect app compatibility:

  • Changes to regular expressions: Invalid group references are now disallowed to more closely follow the semantics of OpenJDK. You might see new cases where an IllegalArgumentException is thrown by the java.util.regex.Matcher class, so make sure to test your app for areas that use regular expressions. To enable or disable this change while testing, toggle the DISALLOW_INVALID_GROUP_REFERENCE flag using the compatibility framework tools.
  • UUID handling: The java.util.UUID.fromString() method now does more strict checks when validating the input argument, so you might see an IllegalArgumentException during deserialization. To enable or disable this change while testing, toggle the ENABLE_STRICT_VALIDATION flag using the compatibility framework tools.
  • ProGuard issues: In some cases, the addition of the java.lang.ClassValue class causes an issue if you try to shrink, obfuscate, and optimize your app using ProGuard. The problem originates with a Kotlin library that changes runtime behaviour based on whether Class.forName("java.lang.ClassValue") returns a class or not. If your app was developed against an older version of the runtime without the java.lang.ClassValue class available, then these optimizations might remove the computeValue method from classes derived from java.lang.ClassValue.

JobScheduler เสริมการทำงานของแฮนเดิลการเรียกกลับและเครือข่าย

นับตั้งแต่เปิดตัว JobScheduler คาดหวังว่าแอปของคุณจะกลับมาจาก onStartJob หรือ onStopJob ภายในไม่กี่วินาที ก่อนที่จะเป็น Android 14 หากงานทำงานนานเกินไป ระบบจะหยุดงานและดำเนินการไม่สำเร็จโดยอัตโนมัติ หากแอปกำหนดเป้าหมายเป็น Android 14 (API ระดับ 34) ขึ้นไป และ เกินเวลาที่ได้รับในเทรดหลัก แอปทำให้เกิด ANR ด้วยข้อความแสดงข้อผิดพลาด "ไม่มีการตอบกลับ onStartJob" หรือ "ไม่ตอบกลับ onStopJob"

ANR นี้อาจเกิดจาก 2 สถานการณ์ ดังนี้ 1. มีงานบล็อกเทรดหลัก ซึ่งทําให้ Callback onStartJob หรือ onStopJob ไม่สามารถดําเนินการและทํางานให้เสร็จภายในเวลาจํากัดที่คาดไว้ 2. นักพัฒนาแอปกำลังทำงานที่บล็อกภายในการเรียกกลับ onStartJob หรือ onStopJob ของ JobScheduler ซึ่งทำให้การเรียกกลับดำเนินการไม่เสร็จภายในเวลาจำกัดที่คาดไว้

ในการแก้ไขข้อ 1 คุณจะต้องแก้ไขข้อบกพร่องของสิ่งที่บล็อกเทรดหลักเพิ่มเติม เมื่อเกิด ANR ขึ้น คุณสามารถทำเช่นนี้ได้โดยใช้ ApplicationExitInfo#getTraceInputStream() เพื่อรับ Tombstone ติดตามเมื่อเกิด ANR หากคุณสร้าง ANR ซ้ำด้วยตนเองได้ คุณสามารถบันทึกการติดตามของระบบและตรวจสอบการติดตามได้โดยใช้ Android Studio หรือ Perfetto เพื่อให้เข้าใจได้ดีขึ้นถึงสิ่งที่กำลังทำงานอยู่ เทรดหลักเมื่อเกิด ANR โปรดทราบว่าปัญหานี้อาจเกิดขึ้นเมื่อใช้ JobScheduler API โดยตรง หรือใช้ WorkManager ซึ่งเป็นไลบรารี androidx

หากต้องการแก้ไขปัญหาที่ 2 ให้ลองเปลี่ยนไปใช้ WorkManager ซึ่งรองรับการรวมการประมวลผลใน onStartJob หรือ onStopJob ในเธรดแบบแอซิงโครนัส

JobScheduler ยังกำหนดให้ต้องประกาศสิทธิ์ ACCESS_NETWORK_STATE ด้วยหากใช้ข้อจำกัด setRequiredNetworkType หรือ setRequiredNetwork หากแอปของคุณไม่ได้ประกาศฟิลด์ สิทธิ์ ACCESS_NETWORK_STATE เมื่อกำหนดเวลางานและกำหนดเป้าหมาย Android 14 ขึ้นไปจะส่งผลให้เกิด SecurityException

API การเปิดตัวไทล์

สำหรับแอปที่กำหนดเป้าหมายเป็น Android 14 ขึ้นไป ระบบจะเลิกใช้งาน TileService#startActivityAndCollapse(Intent) และตอนนี้จะแสดงข้อยกเว้นเมื่อเรียกใช้ หากแอปเปิดกิจกรรมจากการ์ด ให้ใช้ TileService#startActivityAndCollapse(PendingIntent) แทน

ความเป็นส่วนตัว

สิทธิ์เข้าถึงรูปภาพและวิดีโอบางส่วน

Android 14 introduces Selected Photos Access, which allows users to grant apps access to specific images and videos in their library, rather than granting access to all media of a given type.

This change is only enabled if your app targets Android 14 (API level 34) or higher. If you don't use the photo picker yet, we recommend implementing it in your app to provide a consistent experience for selecting images and videos that also enhances user privacy without having to request any storage permissions.

If you maintain your own gallery picker using storage permissions and need to maintain full control over your implementation, adapt your implementation to use the new READ_MEDIA_VISUAL_USER_SELECTED permission. If your app doesn't use the new permission, the system runs your app in a compatibility mode.

ประสบการณ์ของผู้ใช้

รักษาความปลอดภัยให้กับการแจ้งเตือน Intent แบบเต็มหน้าจอ

ใน Android 11 (API ระดับ 30) แอปใดก็ได้ที่จะใช้ Notification.Builder.setFullScreenIntent เพื่อส่ง Intent แบบเต็มหน้าจอได้ขณะที่โทรศัพท์ล็อกอยู่ คุณสามารถให้สิทธิ์นี้โดยอัตโนมัติเมื่อติดตั้งแอปโดยประกาศสิทธิ์ USE_FULL_SCREEN_INTENT ใน AndroidManifest

การแจ้งเตือน Intent แบบเต็มหน้าจอออกแบบมาเพื่อแจ้งเตือนที่มีลำดับความสำคัญสูงมากซึ่งต้องการให้ผู้ใช้สนใจในทันที เช่น การโทรเข้าหรือการตั้งค่านาฬิกาปลุกที่ผู้ใช้กำหนดค่าไว้ สำหรับแอปที่กำหนดเป้าหมายเป็น Android 14 (API ระดับ 34) ขึ้นไป แอปที่ได้รับอนุญาตให้ใช้สิทธิ์นี้จะจำกัดไว้เฉพาะแอปที่มีการโทรและการปลุกเท่านั้น Google Play Store จะเพิกถอนสิทธิ์ USE_FULL_SCREEN_INTENT เริ่มต้นสำหรับแอปที่ไม่ตรงกับโปรไฟล์นี้ กำหนดเวลาสำหรับการเปลี่ยนแปลงนโยบายเหล่านี้คือ31 พฤษภาคม 2024

สิทธิ์นี้จะยังคงเปิดใช้อยู่สำหรับแอปที่ติดตั้งในโทรศัพท์ก่อนที่ผู้ใช้จะอัปเดตเป็น Android 14 ผู้ใช้จะเปิดหรือปิดสิทธิ์นี้ได้

คุณสามารถใช้ API ใหม่ NotificationManager.canUseFullScreenIntent เพื่อตรวจสอบว่าแอปของคุณมีสิทธิ์หรือไม่ หากไม่มี แอปจะใช้ Intent ใหม่ ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT เพื่อเปิดหน้าการตั้งค่าที่ผู้ใช้สามารถให้สิทธิ์ได้

ความปลอดภัย

ข้อจำกัดของ Intent โดยนัยและ Intent ที่รอดำเนินการ

For apps targeting Android 14 (API level 34) or higher, Android restricts apps from sending implicit intents to internal app components in the following ways:

  • Implicit intents are only delivered to exported components. Apps must either use an explicit intent to deliver to unexported components, or mark the component as exported.
  • If an app creates a mutable pending intent with an intent that doesn't specify a component or package, the system throws an exception.

These changes prevent malicious apps from intercepting implicit intents that are intended for use by an app's internal components.

For example, here is an intent filter that could be declared in your app's manifest file:

<activity
    android:name=".AppActivity"
    android:exported="false">
    <intent-filter>
        <action android:name="com.example.action.APP_ACTION" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

If your app tried to launch this activity using an implicit intent, an ActivityNotFoundException exception would be thrown:

Kotlin

// Throws an ActivityNotFoundException exception when targeting Android 14.
context.startActivity(Intent("com.example.action.APP_ACTION"))

Java

// Throws an ActivityNotFoundException exception when targeting Android 14.
context.startActivity(new Intent("com.example.action.APP_ACTION"));

To launch the non-exported activity, your app should use an explicit intent instead:

Kotlin

// This makes the intent explicit.
val explicitIntent =
        Intent("com.example.action.APP_ACTION")
explicitIntent.apply {
    package = context.packageName
}
context.startActivity(explicitIntent)

Java

// This makes the intent explicit.
Intent explicitIntent =
        new Intent("com.example.action.APP_ACTION")
explicitIntent.setPackage(context.getPackageName());
context.startActivity(explicitIntent);

Broadcast Receiver ที่ลงทะเบียนรันไทม์ต้องระบุลักษณะการส่งออก

แอปและบริการที่กำหนดเป้าหมายเป็น Android 14 (API ระดับ 34) ขึ้นไปและใช้ตัวรับที่ลงทะเบียนตามบริบทต้องระบุ Flag เพื่อระบุว่าควรส่งออกตัวรับไปยังแอปอื่นๆ ทั้งหมดในอุปกรณ์หรือไม่ โดยจะใช้ RECEIVER_EXPORTED หรือ RECEIVER_NOT_EXPORTED ตามลำดับ ข้อกำหนดนี้ช่วยปกป้องแอปจากช่องโหว่ด้านความปลอดภัยด้วยการใช้ฟีเจอร์สำหรับรีซีฟเวอร์เหล่านี้ซึ่งเปิดตัวใน Android 13

ข้อยกเว้นสำหรับผู้รับที่รับเฉพาะการออกอากาศของระบบ

หากแอปลงทะเบียนตัวรับสำหรับการออกอากาศของระบบผ่านContext#registerReceiverวิธีต่างๆ เท่านั้น เช่น Context#registerReceiver() ก็ไม่ควรระบุ Flag เมื่อลงทะเบียนตัวรับ

การโหลดโค้ดแบบไดนามิกที่ปลอดภัยยิ่งขึ้น

หากแอปกำหนดเป้าหมายเป็น Android 14 (API ระดับ 34) ขึ้นไปและใช้การโหลดโค้ดแบบไดนามิก (DCL) ไฟล์ที่โหลดแบบไดนามิกทั้งหมดต้องทําเครื่องหมายเป็นแบบอ่านอย่างเดียว มิฉะนั้น ระบบจะยกเว้นข้อยกเว้น เราขอแนะนำให้แอปหลีกเลี่ยงการโหลดโค้ดแบบไดนามิกทุกครั้งที่ทำได้ เนื่องจากการดำเนินการดังกล่าวจะเพิ่มความเสี่ยงอย่างมากที่แอปจะเกิดการประนีประนอมจากการแทรกโค้ดหรือการดัดแปลงโค้ด

หากคุณต้องโหลดโค้ดแบบไดนามิก ให้ใช้วิธีการต่อไปนี้ในการตั้งค่า ไฟล์ที่โหลดแบบไดนามิก (เช่น ไฟล์ DEX, JAR หรือ APK) เป็นแบบอ่านอย่างเดียวในทันที เมื่อไฟล์เปิดขึ้นและก่อนที่จะเขียนเนื้อหา

Kotlin

val jar = File("DYNAMICALLY_LOADED_FILE.jar")
val os = FileOutputStream(jar)
os.use {
    // Set the file to read-only first to prevent race conditions
    jar.setReadOnly()
    // Then write the actual file content
}
val cl = PathClassLoader(jar, parentClassLoader)

Java

File jar = new File("DYNAMICALLY_LOADED_FILE.jar");
try (FileOutputStream os = new FileOutputStream(jar)) {
    // Set the file to read-only first to prevent race conditions
    jar.setReadOnly();
    // Then write the actual file content
} catch (IOException e) { ... }
PathClassLoader cl = new PathClassLoader(jar, parentClassLoader);

จัดการไฟล์ที่โหลดแบบไดนามิกที่มีอยู่แล้ว

หากต้องการป้องกันไม่ให้มีการส่งข้อยกเว้นสำหรับไฟล์ที่โหลดแบบไดนามิกที่มีอยู่ เราขอแนะนำให้ลบและสร้างไฟล์ใหม่ก่อนที่จะลองใช้แบบไดนามิก โหลดส่วนขยายเหล่านั้นอีกครั้งในแอปของคุณ เมื่อคุณสร้างไฟล์ใหม่ ให้ทำตาม คำแนะนำสำหรับการทำเครื่องหมายไฟล์เป็นแบบอ่านอย่างเดียวในขณะที่เขียน อีกวิธีหนึ่งคือ ติดป้ายกำกับไฟล์ที่มีอยู่ใหม่เป็นแบบอ่านอย่างเดียว แต่ในกรณีนี้เราขอแนะนำ ขอแนะนำให้คุณตรวจสอบความสมบูรณ์ของไฟล์ก่อน (ตัวอย่างเช่น ตรวจสอบลายเซ็นของไฟล์กับค่าที่เชื่อถือได้) เพื่อช่วยปกป้องแอปของคุณ จากการกระทำที่เป็นอันตราย

ข้อจำกัดเพิ่มเติมในการเริ่มต้นกิจกรรมจากเบื้องหลัง

For apps targeting Android 14 (API level 34) or higher, the system further restricts when apps are allowed to start activities from the background:

These changes expand the existing set of restrictions to protect users by preventing malicious apps from abusing APIs to start disruptive activities from the background.

Zip Path Traversal

สําหรับแอปที่กําหนดเป้าหมายเป็น Android 14 (API ระดับ 34) ขึ้นไป Android จะป้องกันช่องโหว่ Path Traversal ของไฟล์ ZIP ดังนี้ ZipFile(String) และ ZipInputStream.getNextEntry() จะแสดงข้อผิดพลาด ZipException หากชื่อรายการไฟล์ ZIP มี ".." หรือขึ้นต้นด้วย "/"

แอปสามารถเลือกไม่ใช้การตรวจสอบนี้ได้โดยเรียกใช้ dalvik.system.ZipPathValidator.clearCallback()

สำหรับแอปที่กำหนดเป้าหมายเป็น Android 14 (API ระดับ 34) ขึ้นไป SecurityException จะแสดงขึ้นโดย MediaProjection#createVirtualDisplay ในกรณีต่อไปนี้

แอปของคุณต้องขอให้ผู้ใช้ให้ความยินยอมก่อนเซสชันการจับภาพแต่ละครั้ง เซสชันการบันทึกเดียวคือการเรียกใช้ MediaProjection#createVirtualDisplay ครั้งเดียว และต้องใช้อินสแตนซ์ MediaProjection แต่ละรายการเพียงครั้งเดียว

จัดการการเปลี่ยนแปลงการกำหนดค่า

หากแอปของคุณต้องการเรียกใช้ MediaProjection#createVirtualDisplay เพื่อจัดการการเปลี่ยนแปลงการกำหนดค่า (เช่น การวางแนวหน้าจอหรือการเปลี่ยนแปลงขนาดหน้าจอ) ให้ทำตามขั้นตอนต่อไปนี้เพื่ออัปเดต VirtualDisplay สำหรับอินสแตนซ์ MediaProjection ที่มีอยู่

  1. เรียกใช้ VirtualDisplay#resize โดยใช้ความกว้างและความสูงใหม่
  2. ระบุ Surface ใหม่ที่มีความกว้างและความสูงใหม่ให้กับ VirtualDisplay#setSurface

ลงทะเบียนการโทรกลับ

แอปของคุณควรลงทะเบียนการเรียกกลับเพื่อจัดการกรณีที่ผู้ใช้ไม่ให้ความยินยอมในการบันทึกเซสชันต่อไป โดยให้ใช้ Callback#onStop และแอปของคุณเผยแพร่ทรัพยากรที่เกี่ยวข้อง (เช่น VirtualDisplay และ Surface)

หากแอปไม่ได้ลงทะเบียนการเรียกกลับนี้ MediaProjection#createVirtualDisplay จะแสดง IllegalStateException เมื่อแอปเรียกใช้

ข้อจำกัดที่ไม่ใช่ SDK ที่อัปเดตแล้ว

Android 14 มีรายการอัปเดตของอินเทอร์เฟซที่ไม่ใช่ SDK ซึ่งถูกจำกัด โดยการทำงานร่วมกับนักพัฒนาแอป Android และการทดสอบภายในล่าสุด เราจะตรวจสอบว่ามีทางเลือกอื่นที่เผยแพร่ต่อสาธารณะพร้อมใช้งานก่อนที่จะจำกัดอินเทอร์เฟซที่ไม่ใช่ SDK ทุกครั้งที่ทำได้

หากแอปไม่ได้กำหนดเป้าหมายเป็น Android 14 การเปลี่ยนแปลงบางอย่างเหล่านี้ อาจไม่มีผลกับคุณในทันที อย่างไรก็ตาม แม้ว่าปัจจุบันคุณจะใช้ อินเทอร์เฟซที่ไม่ใช่ SDK บางรายการได้ (ขึ้นอยู่กับระดับ API เป้าหมายของแอป) แต่การใช้เมธอดหรือฟิลด์ที่ไม่ใช่ SDK ใดๆ ก็มีความเสี่ยงสูงที่จะทำให้แอป ขัดข้องเสมอ

หากต้องการดูว่าแอปใช้อินเทอร์เฟซที่ไม่ใช่ SDK อยู่หรือเปล่า คุณสามารถทดสอบแอปดูได้ หากแอปของคุณใช้อินเทอร์เฟซที่ไม่ใช่ SDK คุณควรเริ่มวางแผนย้ายไปใช้ทางเลือกอื่นที่เป็น SDK อย่างไรก็ตาม เราเข้าใจว่าแอปบางแอปมี Use Case ที่ถูกต้องสำหรับการใช้อินเทอร์เฟซที่ไม่ใช่ SDK หากไม่พบวิธีอื่นแทนการใช้อินเทอร์เฟซที่ไม่ใช่ SDK สำหรับฟีเจอร์ในแอป คุณควรขอ API สาธารณะใหม่

ดูข้อมูลเพิ่มเติมเกี่ยวกับการเปลี่ยนแปลงใน Android เวอร์ชันนี้ได้ที่การอัปเดตข้อจํากัดอินเทอร์เฟซที่ไม่ใช่ SDK ใน Android 14 ดูข้อมูลเพิ่มเติมเกี่ยวกับอินเทอร์เฟซที่ไม่ใช่ SDK โดยทั่วไปได้ที่ข้อจำกัดเกี่ยวกับอินเทอร์เฟซที่ไม่ใช่ SDK