在Android 16上,后台状态下调用CameraManager.getCameraCharacteristics() 时崩溃,抛出IllegalArgumentException: Unable to find task ID
以下崩溃仅在Android 16设备上且应用处于后台状态时发生(完全基于Firebase Crashlytics的统计,请查看附带的截图)。
我的应用是一款使用前台服务的视频录制应用,允许在应用处于后台时继续工作(使用摄像头)。
崩溃通过Firebase Crashlytics上报,因此我还无法重现问题或提供一个最小可复现的示例项目。
当前应用大约有60万安装用户。
据Crashlytics显示,截至目前该问题已影响109名用户(共319次事件)。
迄今为止,这个问题发生得很少,我也无法在同一设备上重现。
堆栈跟踪:
Fatal Exception: java.lang.IllegalArgumentException: Unable to find task ID 5429
at android.os.Parcel.createExceptionOrNull(Parcel.java:3377)
at android.os.Parcel.createException(Parcel.java:3357)
at android.os.Parcel.readException(Parcel.java:3340)
at android.os.Parcel.readException(Parcel.java:3282)
at android.app.IAppTask$Stub$Proxy.getTaskInfo(IAppTask.java:249)
at android.app.ActivityManager$AppTask.getTaskInfo(ActivityManager.java:6376)
at android.hardware.camera2.CameraManager.getRotationOverride(CameraManager.java:1674)
at android.hardware.camera2.CameraManager.getRotationOverride(CameraManager.java:1653)
at android.hardware.camera2.CameraManager.getCameraCharacteristics(CameraManager.java:714)
at com.MY_APP_HIDDEN.utilities.CameraUtil.getCameraCharacteristics(CameraUtil.kt:296)
与不同任务ID相似:
Unable to find task ID 147
Unable to find task ID 13649
Unable to find task ID 3420
Unable to find task ID ANY_NUMBER
崩溃发生在以下设备上(大多是Pixel设备,部分为较新的三星机型,以及一台Sony Xperia设备):
Galaxy S10e (Android 16)
Galaxy S23 Ultra (Android 16)
Galaxy S25 Ultra (Android 16)
Galaxy S26 (Android 16)
Galaxy S26 Ultra (Android 16)
Pixel 10 (Android 16)
Pixel 10 Pro (Android 16)
Pixel 10 Pro Fold (Android 16)
Pixel 10 Pro XL (Android 16)
Pixel 6 (Android 16)
Pixel 6 Pro (Android 16)
Pixel 6a (Android 16)
Pixel 7 (Android 16)
Pixel 7 Pro (Android 16)
Pixel 7a (Android 16)
Pixel 8 (Android 16)
Pixel 8 Pro (Android 16)
Pixel 8a (Android 16)
Pixel 9 (Android 16)
Pixel 9 Pro (Android 16)
Pixel 9 Pro Fold (Android 16)
Pixel 9 Pro XL (Android 16)
Pixel 9a (Android 16)
Pixel Fold (Android 16)
Xperia 1 V (Android 16)
尚不清楚为什么 CameraManager.getCameraCharacteristics() 在内部调用 android.app.ActivityManager$AppTask.getTaskInfo
从Android开发者的角度来看,CameraManager.getCameraCharacteristics() 应该在任何时候调用都安全,并且不应依赖应用的任务状态。
解决方案
我在追踪与堆栈跟踪匹配的源代码时遇到了一点困难,但在现在的 main 中,那个 getTaskInfo() 调用就在这里](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/hardware/camera2/CameraManager.java;l=1695;bpv=0;bpt=0):
@FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
@TestApi
public static int getRotationOverrideInternal(@Nullable Context context,
@Nullable PackageManager packageManager, @Nullable String packageName) {
if (!CameraManagerGlobal.sLandscapeToPortrait) {
return ICameraService.ROTATION_OVERRIDE_NONE;
}
if (context != null) {
final ActivityManager activityManager = context.getSystemService(ActivityManager.class);
if (activityManager != null) {
for (ActivityManager.AppTask appTask : activityManager.getAppTasks()) {
final TaskInfo taskInfo = appTask.getTaskInfo();
final int freeformCameraCompatMode = taskInfo.appCompatTaskInfo
.cameraCompatTaskInfo.freeformCameraCompatMode;
if (freeformCameraCompatMode != 0
&& taskInfo.topActivity != null
&& taskInfo.topActivity.getPackageName().equals(packageName)) {
// WindowManager has requested rotation override.
return getRotationOverrideForCompatFreeform(freeformCameraCompatMode);
}
}
}
}
if (packageManager != null && packageName != null) {
try {
return packageManager.getProperty(
PackageManager.PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT,
packageName).getBoolean()
? ICameraService.ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT
: ICameraService.ROTATION_OVERRIDE_NONE;
} catch (PackageManager.NameNotFoundException e) {
// No such property
}
}
return CompatChanges.isChangeEnabled(OVERRIDE_CAMERA_LANDSCAPE_TO_PORTRAIT)
? ICameraService.ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT
: ICameraService.ROTATION_OVERRIDE_NONE;
}
看起来他们似乎在尝试判断相机输出是否需要旋转,以支持自由形态多窗口支持。我能理解这可能与任务相关。但 getTaskInfo() 是一个脆弱的调用;在我看来,如果无法获取任务信息,应该进行优雅降级。
我看到你(?) 提交了一个错误报告,希望他们能解决这个问题。与此同时,您可能需要在 getCameraCharacteristics() 周围添加您自己的异常处理逻辑,并做一些处理(例如使用您缓存的最近已知值)。
