應用安裝到 Android 14 上,出現如下提示
This app isn’t compatible with the latest version of Android. Check for an update or contact the app’s developer.
通過源碼找原因。
提示的字符
根據字符找到 ./frameworks/base/core/res/res/values/strings.xml
,
<!-- Message displayed in dialog when app is 32 bit on a 64 bit system. [CHAR LIMIT=NONE] -->
<string name="deprecated_abi_message">This app isn\'t compatible with the latest version of Android. Check for an update or contact the app\'s developer.</string>
對應的中文 ./frameworks/base/core/res/res/values-zh-rCN/strings.xml
,
<string name="deprecated_abi_message" msgid="6820548011196218091">"此應用與最新版 Android 不兼容。請檢查是否有更新,或與應用開發者聯系。"</string>
DeprecatedAbiDialog
彈窗UI在 ./frameworks/base/services/core/java/com/android/server/wm/DeprecatedAbiDialog.java
,
package com.android.server.wm;import android.app.AlertDialog;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.view.Window;
import android.view.WindowManager;import com.android.internal.R;class DeprecatedAbiDialog extends AppWarnings.BaseDialog {DeprecatedAbiDialog(final AppWarnings manager, Context context,ApplicationInfo appInfo) {super(manager, appInfo.packageName);final PackageManager pm = context.getPackageManager();final CharSequence label = appInfo.loadSafeLabel(pm,PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE| PackageItemInfo.SAFE_LABEL_FLAG_TRIM);final CharSequence message = context.getString(R.string.deprecated_abi_message);final AlertDialog.Builder builder = new AlertDialog.Builder(context).setPositiveButton(R.string.ok, (dialog, which) ->manager.setPackageFlag(mPackageName, AppWarnings.FLAG_HIDE_DEPRECATED_ABI, true)).setMessage(message).setTitle(label);// Ensure the content view is prepared.mDialog = builder.create();mDialog.create();final Window window = mDialog.getWindow();window.setType(WindowManager.LayoutParams.TYPE_PHONE);// DO NOT MODIFY. Used by CTS to verify the dialog is displayed.window.getAttributes().setTitle("DeprecatedAbiDialog");}
}
AppWarnings
調用到 DeprecatedAbiDialog
的是 ./frameworks/base/services/core/java/com/android/server/wm/AppWarnings.java
,
showDeprecatedAbiDialogIfNeeded
很顯然,如果設備是64位的,但是app只有32位的so庫,就會出現這個彈窗。
還可以看到,可以通過調試的方法,關閉這個彈窗檢測。
即 setprop debug.wm.disable_deprecated_abi_dialog true
/*** Shows the "deprecated abi" warning, if necessary. This can only happen is the device* supports both 64-bit and 32-bit ABIs, and the app only contains 32-bit libraries. The app* cannot be installed if the device only supports 64-bit ABI while the app contains only 32-bit* libraries.** @param r activity record for which the warning may be displayed*/public void showDeprecatedAbiDialogIfNeeded(ActivityRecord r) {final boolean isUsingAbiOverride = (r.info.applicationInfo.privateFlagsExt& ApplicationInfo.PRIVATE_FLAG_EXT_CPU_OVERRIDE) != 0;if (isUsingAbiOverride) {// The abiOverride flag was specified during installation, which means that if the app// is currently running in 32-bit mode, it is intended. Do not show the warning dialog.return;}// The warning dialog can also be disabled for debugging purposefinal boolean disableDeprecatedAbiDialog = SystemProperties.getBoolean("debug.wm.disable_deprecated_abi_dialog", false);if (disableDeprecatedAbiDialog) {return;}final String appPrimaryAbi = r.info.applicationInfo.primaryCpuAbi;final String appSecondaryAbi = r.info.applicationInfo.secondaryCpuAbi;final boolean appContainsOnly32bitLibraries =(appPrimaryAbi != null && appSecondaryAbi == null && !appPrimaryAbi.contains("64"));final boolean is64BitDevice =ArrayUtils.find(Build.SUPPORTED_ABIS, abi -> abi.contains("64")) != null;if (is64BitDevice && appContainsOnly32bitLibraries) {mUiHandler.showDeprecatedAbiDialog(r);}}
showDeprecatedAbiDialogUiThread
handle 的處理,調用到 showDeprecatedAbiDialogUiThread
,
/*** Handles messages on the system process UI thread.*/private final class UiHandler extends Handler {private static final int MSG_SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG = 1;private static final int MSG_HIDE_UNSUPPORTED_DISPLAY_SIZE_DIALOG = 2;private static final int MSG_SHOW_UNSUPPORTED_COMPILE_SDK_DIALOG = 3;private static final int MSG_HIDE_DIALOGS_FOR_PACKAGE = 4;private static final int MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG = 5;private static final int MSG_SHOW_DEPRECATED_ABI_DIALOG = 6;public UiHandler(Looper looper) {super(looper, null, true);}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {//...case MSG_SHOW_DEPRECATED_ABI_DIALOG: {final ActivityRecord ar = (ActivityRecord) msg.obj;showDeprecatedAbiDialogUiThread(ar);} break;}}//...}/*** Shows the "deprecated abi" warning for the given application.* <p>* <strong>Note:</strong> Must be called on the UI thread.** @param ar record for the activity that triggered the warning*/@UiThreadprivate void showDeprecatedAbiDialogUiThread(ActivityRecord ar) {if (mDeprecatedAbiDialog != null) {mDeprecatedAbiDialog.dismiss();mDeprecatedAbiDialog = null;}if (ar != null && !hasPackageFlag(ar.packageName, FLAG_HIDE_DEPRECATED_ABI)) {mDeprecatedAbiDialog = new DeprecatedAbiDialog(AppWarnings.this, mUiContext, ar.info.applicationInfo);mDeprecatedAbiDialog.show();}}
拓展
來都來的,順道看到了 DeprecatedTargetSdkVersionDialog
的邏輯處理,
/*** Shows the "deprecated target sdk" warning, if necessary.** @param r activity record for which the warning may be displayed*/public void showDeprecatedTargetDialogIfNeeded(ActivityRecord r) {if (r.info.applicationInfo.targetSdkVersion < Build.VERSION.MIN_SUPPORTED_TARGET_SDK_INT) {mUiHandler.showDeprecatedTargetDialog(r);}}/*** Shows the "deprecated target sdk version" warning for the given application.* <p>* <strong>Note:</strong> Must be called on the UI thread.** @param ar record for the activity that triggered the warning*/@UiThreadprivate void showDeprecatedTargetSdkDialogUiThread(ActivityRecord ar) {if (mDeprecatedTargetSdkVersionDialog != null) {mDeprecatedTargetSdkVersionDialog.dismiss();mDeprecatedTargetSdkVersionDialog = null;}if (ar != null && !hasPackageFlag(ar.packageName, FLAG_HIDE_DEPRECATED_SDK)) {mDeprecatedTargetSdkVersionDialog = new DeprecatedTargetSdkVersionDialog(AppWarnings.this, mUiContext, ar.info.applicationInfo);mDeprecatedTargetSdkVersionDialog.show();}}
如果 app 的 targetSdkVersion
版本低于平臺支持的最小sdk版本(ro.build.version.min_supported_target_sdk
),就會提示:
此應用專為舊版 Android 系統打造。它可能無法正常運行,也不包含最新的安全和隱私保護功能。請檢查是否有更新,或與應用開發者聯系。
對應的字符是 ./frameworks/base/core/res/res/values/strings.xml
里的 deprecated_target_sdk_message
android$ cat ./frameworks/base/core/res/res/values-zh-rCN/strings.xml | grep deprecated_target_sdk_message<string name="deprecated_target_sdk_message" msgid="5246906284426844596">"此應用專為舊版 Android 系統打造。它可能無法正常運行,也不包含最新的安全和隱私保護功能。請檢查是否有更新,或與應用開發者聯系。"</string>
android$
android$ cat ./frameworks/base/core/res/res/values/strings.xml | grep deprecated_target_sdk_message<string name="deprecated_target_sdk_message">This app was built for an older version of Android. It might not work properly and doesn\'t include the latest security and privacy protections. Check for an update, or contact the app\'s developer.</string>
最終顯示dialog
/*** Shows the "deprecated abi" warning for the given application.* <p>* <strong>Note:</strong> Must be called on the UI thread.** @param ar record for the activity that triggered the warning*/@UiThreadprivate void showDeprecatedAbiDialogUiThread(ActivityRecord ar) {if (mDeprecatedAbiDialog != null) {mDeprecatedAbiDialog.dismiss();mDeprecatedAbiDialog = null;}if (ar != null && !hasPackageFlag(ar.packageName, FLAG_HIDE_DEPRECATED_ABI)) {mDeprecatedAbiDialog = new DeprecatedAbiDialog(AppWarnings.this, mUiContext, ar.info.applicationInfo);mDeprecatedAbiDialog.show();}}
解決辦法
使 app 支持64位的庫。
修改 app 的 build.gradle , 添加 “arm64-v8a” ,
externalNativeBuild {cmake {//abiFilters "armeabi-v7a"abiFilters "armeabi-v7a","arm64-v8a"cppFlags "-std=c++11 -frtti -fexceptions"arguments "-DANDROID_STL=c++_static"}}ndk {//abiFilters "armeabi-v7a"abiFilters "armeabi-v7a","arm64-v8a"stl "stlport_shared"}