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

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

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

الوظيفة الأساسية

أنواع الخدمات التي تعمل في المقدّمة مطلوبة

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

إذا لم تكن حالة استخدام في تطبيقك مرتبطة بأيّ من هذه الأنواع، ننصح بشدة بنقل منطقك لاستخدام WorkManager أو مهام نقل البيانات التي يبدأها المستخدم.

فرض إذن 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 سلوك رد الاتصال والشبكة

تتوقع أداة Jobscheduler منذ إطلاقها أن يعود تطبيقك onStartJob أو onStopJob خلال بضع ثوانٍ. قبل Android 14، إذا كانت المهمة تستغرق وقتًا طويلاً، يتم إيقاف المهمة وتفشل تلقائيًا. إذا كان تطبيقك يستهدف الإصدار 14 من نظام التشغيل Android (المستوى 34 لواجهة برمجة التطبيقات) أو إصدارًا أحدث وتجاوز المدّة الممنوحة في سلسلة المهام الرئيسية، سيُنشئ التطبيق خطأ ANR مع رسالة الخطأ "ما مِن استجابة لـ onStartJob" أو "ما مِن استجابة لـ onStopJob".

قد يكون خطأ ANR هذا نتيجة لأحد السيناريوهَين التاليَين: 1. هناك عمل يحظر سلسلة المحادثات الرئيسية، ما يمنع عمليات معاودة الاتصال onStartJob. أو onStopJob من التنفيذ والإكمال خلال المهلة الزمنية المتوقّعة. 2. ينفِّذ المطوّر عمل حظر ضمن Job Scheduler استدعاء onStartJob أو onStopJob، ما يمنع معاودة الاتصال من لإنجازها خلال الحد الزمني المتوقع.

لمعالجة رقم 1، عليك إجراء المزيد من تصحيح الأخطاء التي تمنع سلسلة التعليمات الرئيسية. عند حدوث خطأ ANR، يمكنك القيام بذلك باستخدام ApplicationExitInfo#getTraceInputStream() للحصول على شاهد القبر وتتبع حدوث خطأ ANR. إذا كان بإمكانك إعادة إنتاج خطأ ANR يدويًا، يمكنك تسجيل تتبُّع للنظام وفحص التتبُّع باستخدام Android Studio أو Perfetto لفهم ما الذي يتم تشغيله في الخيط الرئيسي عند حدوث خطأ ANR بشكل أفضل. يُرجى العِلم أنّ هذا يمكن أن يحدث عند استخدام واجهة برمجة التطبيقات Jobscheduler API مباشرةً. أو استخدام WorkManager لمكتبة androidx.

لمعالجة رقم 2، ننصحك بإجراء عملية النقل إلى WorkManager، التي توفّر توفير إمكانية إكمال أي معالجة في onStartJob أو onStopJob في سلسلة محادثات غير متزامنة.

تفرض JobScheduler أيضًا شرطًا للإفصاح عن الإذن ACCESS_NETWORK_STATE في حال استخدام قيد setRequiredNetworkType أو setRequiredNetwork. إذا لم يفصح تطبيقك عن الحصول على إذن "ACCESS_NETWORK_STATE" عند تحديد موعد المهمة وتحديد الاستهداف الإصدار 14 من نظام التشغيل Android أو الإصدارات الأحدث، سيؤدي إلى حدوث SecurityException.

Tiles launch API

بالنسبة إلى التطبيقات التي تستهدف 14 عامًا أو أعلى، تم إيقاف TileService#startActivityAndCollapse(Intent) نهائيًا وسيتم طرحه الآن. استثناءً عند استدعائه. إذا كان تطبيقك يطلق الأنشطة من الشاشات، استخدِم TileService#startActivityAndCollapse(PendingIntent) بدلاً من ذلك.

الخصوصية

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

يقدّم نظام التشغيل Android 14 ميزة "الوصول إلى الصور المحدّدة" التي تتيح للمستخدمين منح التطبيقات إذنًا بالوصول إلى صور وفيديوهات معيّنة في مكتبتهم، بدلاً من منح إذن بالوصول إلى كل الوسائط من نوع معيّن.

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

إذا كنت تحافظ على أداة اختيار معرض الصور باستخدام أذونات التخزين وتحتاج إلى الحفاظ على التحكّم الكامل في عملية التنفيذ، عليك تعديل عملية التنفيذ لاستخدام الإذن الجديد READ_MEDIA_VISUAL_USER_SELECTED. إذا كان تطبيقك لا يستخدم الإذن الجديد، سيشغّل النظام تطبيقك في وضع ملف التوافق.

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

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

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.

الأمان

القيود المفروضة على الأهداف الضمنية والمعلّقة

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

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

على التطبيقات والخدمات التي تستهدف الإصدار 14 من نظام التشغيل Android (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث وتستخدم مستقبلات مسجّلة في السياق تحديد علامة للإشارة إلى ما إذا كان يجب تصدير المستقبل إلى جميع التطبيقات الأخرى على الجهاز أم لا: إما RECEIVER_EXPORTED أو RECEIVER_NOT_EXPORTED، على التوالي. يساعد هذا الشرط في حماية التطبيقات من الثغرات الأمنية من خلال الاستفادة من ميزات هذه الأجهزة المستلِمة التي تم تقديمها في Android 13.

استثناء لأجهزة الاستقبال التي تتلقّى عمليات بث النظام فقط

إذا كان تطبيقك يسجِّل جهاز استقبال فقط لتلقّي عمليات بث النظام من خلال Context#registerReceiver طُرق، مثل Context#registerReceiver()، يجب عدم تحديد علامة عند تسجيل جهاز الاستقبال.

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

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.

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

بالنسبة إلى التطبيقات التي تستهدف الإصدار 14 من نظام التشغيل Android (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، يفرض النظام المزيد من القيود على الحالات التي يُسمح للتطبيقات فيها ببدء الأنشطة من الخلفية:

  • عندما يُرسِل تطبيق طلبًا باستخدام PendingIntent PendingIntent#send() أو طُرق مشابهة، يجب أن يوافق التطبيق على منح امتيازات بدء النشاط في الخلفية لبدء الطلب المعلّق. للموافقة على الميزة، يجب أن يُرسل التطبيق ActivityOptions حزمة تحتوي على setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED).
  • عندما يربط تطبيق مرئي خدمة تطبيق آخر يعمل في الخلفية باستخدام طريقة bindService()، يجب أن يوافق التطبيق المرئي الآن على ذلك إذا كان يريد منح امتيازات تشغيل النشاط في الخلفية الخاصة به للخدمة المرتبطة. للموافقة على الميزة، يجب أن يتضمّن التطبيق العلامة BIND_ALLOW_ACTIVITY_STARTS عند استدعاء الأسلوب bindService().

تعمل هذه التغييرات على توسيع مجموعة القيود الحالية لحماية المستخدِمين من خلال منع التطبيقات الضارة من إساءة استخدام واجهات برمجة التطبيقات لبدء أنشطة مزعجة من الخلفية.

مسح مسار ملفات Zip

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

يمكن للتطبيقات إيقاف عملية التحقّق هذه من خلال الاتصال بالرقم dalvik.system.ZipPathValidator.clearCallback().

بالنسبة إلى التطبيقات التي تستهدف الإصدار 14 من نظام التشغيل Android (المستوى 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.

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

إذا لم تكن متأكدًا مما إذا كان تطبيقك يستخدم واجهات غير متوفرة في حزمة SDK، يمكنك اختبار تطبيقك لمعرفة ذلك. إذا كان تطبيقك يعتمد على واجهات غير متوفرة في حزمة SDK، عليك البدء في التخطيط لنقل البيانات إلى بدائل حزمة SDK. ومع ذلك، ندرك أنّ بعض التطبيقات لديها حالات استخدام صالحة لواجهات غير متوفرة في حزمة SDK. إذا لم تتمكّن من العثور على بديل لاستخدام واجهة غير متوفّرة في حزمة SDK لإحدى الميزات في تطبيقك، عليك طلب واجهة برمجة تطبيقات عامة جديدة.

لمزيد من المعلومات عن التغييرات في هذا الإصدار من Android، اطّلِع على تعديلات على القيود المفروضة على الواجهات غير المستندة إلى حزمة SDK في Android 14. للاطّلاع على مزيد من المعلومات حول الواجهات غير المتوفرة في حزمة SDK بشكل عام، اطّلِع على مقالة القيود المفروضة على الواجهات غير المتوفرة في حزمة SDK.