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

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

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

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

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

หากแอปกำหนดเป้าหมายเป็น Android 14 (API ระดับ 34) ขึ้นไป แอปต้องระบุประเภทบริการที่ทำงานอยู่เบื้องหน้าอย่างน้อย 1 ประเภทสำหรับบริการที่ทำงานอยู่เบื้องหน้าแต่ละรายการภายในแอป คุณควรเลือกประเภทบริการที่ทำงานอยู่เบื้องหน้าที่แสดงถึง Use Case ของแอป ระบบคาดหวังว่าบริการที่ทำงานอยู่เบื้องหน้าซึ่งมีประเภทหนึ่งๆ จะเป็นไปตาม Use Case ที่เฉพาะเจาะจง

หากกรณีการใช้งานในแอปของคุณไม่เกี่ยวข้องกับประเภทใดเลย เราขอแนะนำให้ย้ายข้อมูลตรรกะของคุณเพื่อใช้ WorkManager หรือการโอนข้อมูลที่เริ่มต้นโดยผู้ใช้

การบังคับใช้สิทธิ์ 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 เปิดตัวการเข้าถึงรูปภาพที่เลือก ซึ่งช่วยให้ผู้ใช้สามารถให้สิทธิ์แอปเข้าถึงรูปภาพและวิดีโอที่เฉพาะเจาะจงในคลังได้ แทนที่จะให้สิทธิ์เข้าถึงสื่อทั้งหมดในประเภทหนึ่งๆ

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

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

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

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

With Android 11 (API level 30), it was possible for any app to use Notification.Builder.setFullScreenIntent to send full-screen intents while the phone is locked. You could auto-grant this on app install by declaring USE_FULL_SCREEN_INTENT permission in the AndroidManifest.

Full-screen intent notifications are designed for extremely high-priority notifications demanding the user's immediate attention, such as an incoming phone call or alarm clock settings configured by the user. For apps targeting Android 14 (API level 34) or higher, apps that are allowed to use this permission are limited to those that provide calling and alarms only. The Google Play Store revokes default USE_FULL_SCREEN_INTENT permissions for any apps that don't fit this profile. The deadline for these policy changes is May 31, 2024.

This permission remains enabled for apps installed on the phone before the user updates to Android 14. Users can turn this permission on and off.

You can use the new API NotificationManager.canUseFullScreenIntent to check if your app has the permission; if not, your app can use the new intent ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT to launch the settings page where users can grant the permission.

ความปลอดภัย

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

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

  • ระบบจะส่ง Intent ที่ไม่ชัดแจ้งไปยังคอมโพเนนต์ที่ส่งออกเท่านั้น แอปต้องใช้ Intent ที่ชัดเจนเพื่อนำส่งไปยังคอมโพเนนต์ที่ไม่ได้ส่งออก หรือทำเครื่องหมายคอมโพเนนต์ว่าส่งออกแล้ว
  • หากแอปสร้าง PendingIntent ที่เปลี่ยนแปลงได้โดยมี Intent ที่ไม่ได้ระบุคอมโพเนนต์หรือแพ็กเกจ ระบบจะแสดงข้อยกเว้น

การเปลี่ยนแปลงเหล่านี้จะช่วยป้องกันไม่ให้แอปที่เป็นอันตรายขัดขวาง Intent แบบไม่เจาะจงปลายทางที่มีไว้สำหรับให้คอมโพเนนต์ภายในของแอปใช้งาน

ต่อไปนี้คือตัวอย่างตัวกรอง Intent ที่ประกาศได้ในไฟล์ Manifest ของแอป

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

หากแอปพยายามเปิดใช้งานกิจกรรมนี้โดยใช้ Intent ที่ไม่ชัด ระบบจะแสดงข้อยกเว้น ActivityNotFoundException ดังนี้

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"));

หากต้องการเปิดกิจกรรมที่ไม่ได้ส่งออก แอปของคุณควรใช้ Intent แบบเจาะจงแทน ดังนี้

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 ที่ลงทะเบียนรันไทม์ต้องระบุลักษณะการส่งออก

Apps and services that target Android 14 (API level 34) or higher and use context-registered receivers are required to specify a flag to indicate whether or not the receiver should be exported to all other apps on the device: either RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED, respectively. This requirement helps protect apps from security vulnerabilities by leveraging the features for these receivers introduced in Android 13.

Exception for receivers that receive only system broadcasts

If your app is registering a receiver only for system broadcasts through Context#registerReceiver methods, such as Context#registerReceiver(), then it shouldn't specify a flag when registering the receiver.

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

หากแอปกำหนดเป้าหมายเป็น 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);

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

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

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

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

  • เมื่อแอปส่ง PendingIntent โดยใช้ PendingIntent#send() หรือวิธีการที่คล้ายกัน แอปต้องเลือกใช้หากต้องการมอบสิทธิ์การเริ่มกิจกรรมเบื้องหลังของตนเองเพื่อเริ่ม Intent ที่รอดำเนินการ หากต้องการเลือกใช้ แอปควรส่ง ActivityOptions Bundle ที่มีsetPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
  • เมื่อแอปที่มองเห็นได้เชื่อมโยงบริการของแอปอื่นที่อยู่ในเบื้องหลังโดยใช้เมธอด bindService() ตอนนี้แอปที่มองเห็นได้ต้องเลือกใช้หากต้องการมอบสิทธิ์การเริ่มกิจกรรมเบื้องหลังของตนเองให้กับบริการที่เชื่อมโยง หากต้องการเลือกใช้ แอปควรระบุ Flag BIND_ALLOW_ACTIVITY_STARTS เมื่อเรียกใช้เมธอด bindService()

การเปลี่ยนแปลงเหล่านี้จะขยายชุดข้อจำกัดที่มีอยู่เพื่อปกป้องผู้ใช้โดยการป้องกันการที่แอปที่เป็นอันตรายจะละเมิด API เพื่อเริ่มกิจกรรมที่รบกวนจากเบื้องหลัง

Zip Path Traversal

For apps targeting Android 14 (API level 34) or higher, Android prevents the Zip Path Traversal Vulnerability in the following way: ZipFile(String) and ZipInputStream.getNextEntry() throws a ZipException if zip file entry names contain ".." or start with "/".

Apps can opt-out from this validation by calling dalvik.system.ZipPathValidator.clearCallback().

For apps targeting Android 14 (API level 34) or higher, a SecurityException is thrown by MediaProjection#createVirtualDisplay in either of the following scenarios:

Your app must ask the user to give consent before each capture session. A single capture session is a single invocation on MediaProjection#createVirtualDisplay, and each MediaProjection instance must be used only once.

Handle configuration changes

If your app needs to invoke MediaProjection#createVirtualDisplay to handle configuration changes (such as the screen orientation or screen size changing), you can follow these steps to update the VirtualDisplay for the existing MediaProjection instance:

  1. Invoke VirtualDisplay#resize with the new width and height.
  2. Provide a new Surface with the new width and height to VirtualDisplay#setSurface.

Register a callback

Your app should register a callback to handle cases where the user doesn't grant consent to continue a capture session. To do this, implement Callback#onStop and have your app release any related resources (such as the VirtualDisplay and Surface).

If your app doesn't register this callback, MediaProjection#createVirtualDisplay throws an IllegalStateException when your app invokes it.

ข้อจำกัดที่ไม่ใช่ 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