التغييرات في السلوك: التطبيقات التي تستهدف الإصدار 14 من نظام التشغيل Android أو الإصدارات الأحدث

كما هو الحال مع الإصدارات السابقة، يتضمّن Android 14 تغييرات في السلوك قد تؤثّر في تطبيقك. تنطبق تغييرات السلوك التالية حصريًا على التطبيقات التي تستهدف الإصدار 14 من نظام التشغيل Android (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث. إذا كان تطبيقك يستهدف الإصدار 14 من نظام التشغيل Android أو الإصدارات الأحدث، عليك تعديل تطبيقك ليتوافق مع هذه السلوكيات بشكل صحيح، حيثما ينطبق ذلك.

احرص أيضًا على مراجعة قائمة التغييرات في السلوك التي تؤثّر في جميع التطبيقات التي تعمل على 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

يفرض الإصدار 14 من نظام التشغيل Android إذن BLUETOOTH_CONNECT عند استدعاء الأسلوب BluetoothAdapter getProfileConnectionState() للتطبيقات التي تستهدف Android 14 (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث.

سبق أن تطلبت هذه الطريقة الحصول على إذن BLUETOOTH_CONNECT، ولكن لم يتم فرضها. تأكَّد من أنّ تطبيقك يعلن عن BLUETOOTH_CONNECT في ملف AndroidManifest.xml الخاص به كما هو موضّح في المقتطف التالي، وتحقَّق من أنّه منح المستخدم الإذن قبل استدعاء getProfileConnectionState.

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

تعديلات على OpenJDK 17

يواصل نظام التشغيل Android 14 العمل على تحديث المكتبات الأساسية في Android لمواءمتها مع الميزات في أحدث إصدارات OpenJDK LTS، بما في ذلك تحديثات مكتبة ودعم لغة Java 17 لمطوّري التطبيقات والمنصات.

يمكن أن تؤثّر بعض هذه التغييرات في توافق التطبيق:

  • تغييرات على التعبيرات العادية: تم الآن منع استخدام مراجع المجموعات غير الصالحة لتتمكّن من اتّباع الدلالات في OpenJDK بشكلٍ أدق. قد تظهر لك حالات جديدة يتم فيها طرح IllegalArgumentException من فئة java.util.regex.Matcher، لذا احرص على اختبار تطبيقك في المناطق التي تستخدم التعبيرات العادية. لتفعيل هذا التغيير أو إيقافه أثناء الاختبار، بدِّل العلامة DISALLOW_INVALID_GROUP_REFERENCE باستخدام أدوات إطار العمل المعني بالتوافق.
  • معالجة معرّف UUID: تُجري طريقة java.util.UUID.fromString() الآن عمليات تحقّق أكثر صرامة عند التحقّق من مَعلمة الإدخال، لذا قد يظهر لك IllegalArgumentException أثناء تحويل البيانات إلى نص. لتفعيل هذا التغيير أو إيقافه أثناء الاختبار، بدِّل العلامة ENABLE_STRICT_VALIDATION باستخدام أدوات إطار العمل المعني بالتوافق.
  • مشاكل ProGuard: في بعض الحالات، تؤدي إضافة فئة java.lang.ClassValue إلى حدوث مشكلة إذا حاولت تصغير تطبيقك وتشويشه وتحسينه باستخدام ProGuard. تنشأ المشكلة من مكتبة Kotlin التي تغيّر سلوك وقت التشغيل استنادًا إلى ما إذا كانت Class.forName("java.lang.ClassValue") تُرجع فئة أم لا. إذا تم تطوير تطبيقك باستخدام إصدار قديم من وقت التشغيل بدون توفّر فئة java.lang.ClassValue، قد تؤدي عمليات التحسين هذه إلى إزالة طريقة computeValue من الفئات المشتقة من java.lang.ClassValue.

تعزّز JobScheduler سلوك عمليات رد الاتصال والشبكة

Since its introduction, JobScheduler expects your app to return from onStartJob or onStopJob within a few seconds. Prior to Android 14, if a job runs too long, the job is stopped and fails silently. If your app targets Android 14 (API level 34) or higher and exceeds the granted time on the main thread, the app triggers an ANR with the error message "No response to onStartJob" or "No response to onStopJob".

This ANR may be a result of 2 scenarios: 1. There is work blocking the main thread, preventing the callbacks onStartJob or onStopJob from executing and completing within the expected time limit. 2. The developer is running blocking work within the JobScheduler callback onStartJob or onStopJob, preventing the callback from completing within the expected time limit.

To address #1, you will need to further debug what is blocking the main thread when the ANR occurs, you can do this using ApplicationExitInfo#getTraceInputStream() to get the tombstone trace when the ANR occurs. If you're able to manually reproduce the ANR, you can record a system trace and inspect the trace using either Android Studio or Perfetto to better understand what is running on the main thread when the ANR occurs. Note that this can happen when using JobScheduler API directly or using the androidx library WorkManager.

To address #2, consider migrating to WorkManager, which provides support for wrapping any processing in onStartJob or onStopJob in an asynchronous thread.

JobScheduler also introduces a requirement to declare the ACCESS_NETWORK_STATE permission if using setRequiredNetworkType or setRequiredNetwork constraint. If your app does not declare the ACCESS_NETWORK_STATE permission when scheduling the job and is targeting Android 14 or higher, it will result in a SecurityException.

Tiles launch API

For apps targeting 14 and higher, TileService#startActivityAndCollapse(Intent) is deprecated and now throws an exception when called. If your app launches activities from tiles, use TileService#startActivityAndCollapse(PendingIntent) instead.

الخصوصية

الوصول الجزئي إلى الصور والفيديوهات

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.

تجربة المستخدم

إشعارات آمنة بوضع العرض بملء الشاشة

في الإصدار 11 من Android (المستوى 30 لواجهة برمجة التطبيقات)، كان بإمكان أي تطبيق استخدام Notification.Builder.setFullScreenIntent لإرسال رسائل بهدف عرض المحتوى بملء الشاشة عندما يكون الهاتف مقفلاً. يمكنك منح هذا الإذن تلقائيًا عند تثبيت التطبيق من خلال تحديد إذن USE_FULL_SCREEN_INTENT فيملف AndroidManifest.

تم تصميم إشعارات العرض بملء الشاشة لإرسال إشعارات مهمة للغاية تتطلّب اهتمام المستخدم الفوري، مثل مكالمة هاتفية واردة أو إعدادات المنبّه التي ضبطها المستخدم. بالنسبة إلى التطبيقات التي تستهدف الإصدار Android 14 (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، تقتصر التطبيقات المسموح لها باستخدام هذا الإذن على تلك التي توفّر وظائف الاتصال والمنبّهات فقط. يُلغي "متجر Google Play" أذونات USE_FULL_SCREEN_INTENT التلقائية لأي تطبيقات لا تتوافق مع هذا الملف الشخصي. الموعد النهائي لتنفيذ هذه التغييرات في السياسة هو 31 أيار (مايو) 2024.

يظل هذا الإذن مفعّلاً للتطبيقات المثبَّتة على الهاتف قبل أن يصعّد المستخدم إلى الإصدار 14 من Android. يمكن للمستخدمين تفعيل هذا الإذن أو إيقافه.

يمكنك استخدام واجهة برمجة التطبيقات الجديدة NotificationManager.canUseFullScreenIntent للتحقّق مما إذا كان تطبيقك يملك الإذن. وإذا لم يكن الأمر كذلك، يمكن لتطبيقك استخدام الطلب الجديد ACTION_MANAGE_APP_USE_FULL_SCREEN_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);

يجب أن تحدّد مستقبِلات البث المسجّلة في وقت التشغيل سلوك التصدير

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.

تحميل الرموز الديناميكية بشكل أكثر أمانًا

If your app targets Android 14 (API level 34) or higher and uses Dynamic Code Loading (DCL), all dynamically-loaded files must be marked as read-only. Otherwise, the system throws an exception. We recommend that apps avoid dynamically loading code whenever possible, as doing so greatly increases the risk that an app can be compromised by code injection or code tampering.

If you must dynamically load code, use the following approach to set the dynamically-loaded file (such as a DEX, JAR, or APK file) as read-only as soon as the file is opened and before any content is written:

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

Handle dynamically-loaded files that already exist

To prevent exceptions from being thrown for existing dynamically-loaded files, we recommend deleting and recreating the files before you try to dynamically load them again in your app. As you recreate the files, follow the preceding guidance for marking the files read-only at write time. Alternatively, you can re-label the existing files as read-only, but in this case, we strongly recommend that you verify the integrity of the files first (for example, by checking the file's signature against a trusted value), to help protect your app from malicious actions.

قيود إضافية على بدء الأنشطة من الخلفية

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

بالنسبة إلى التطبيقات التي تستهدف الإصدار 14 من نظام التشغيل Android (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، يمنع Android ثغرة ملف ZIP Path Traversal بالطريقة التالية: يعرض كل من ZipFile(String) و ZipInputStream.getNextEntry() خطأ ZipException إذا كانت أسماء إدخالات ملفات ZIP تحتوي على ".." أو تبدأ بـ "/".

يمكن للتطبيقات إيقاف عملية التحقّق هذه من خلال الاتصال بالرقم 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 includes updated lists of restricted non-SDK interfaces based on collaboration with Android developers and the latest internal testing. Whenever possible, we make sure that public alternatives are available before we restrict non-SDK interfaces.

If your app does not target Android 14, some of these changes might not immediately affect you. However, while you can currently use some non-SDK interfaces (depending on your app's target API level), using any non-SDK method or field always carries a high risk of breaking your app.

If you are unsure if your app uses non-SDK interfaces, you can test your app to find out. If your app relies on non-SDK interfaces, you should begin planning a migration to SDK alternatives. Nevertheless, we understand that some apps have valid use cases for using non-SDK interfaces. If you cannot find an alternative to using a non-SDK interface for a feature in your app, you should request a new public API.

To learn more about the changes in this release of Android, see Updates to non-SDK interface restrictions in Android 14. To learn more about non-SDK interfaces generally, see Restrictions on non-SDK interfaces.