把之前开发遇到的一些问题总结一下, 由于很多东西都是和具体平台(高通/MKT)相关的, 本来有更多的内容可以总结, 但由于不在之前公司了,一些源码和环境都没了, 只能写一下记得比较清楚的东西了.
dumpsys
dumpsys
是Android系统中一个调试神器, 可以看内存信息, 电池信息, 相机参数等等, 基本Android中每一个大的模块, 都能通过dumpsys
来查看. 调试Camera我们通常需要知道一些参数信息, 默认参数, 实际设置参数, 支持的参数, 这些信息都可以通过dumpsys media.camera
来查看, 具体用法如下:
//执行这个adb命令会打印所有默认参数和当前使用参数
adb shell dumpsys media.camera
//查看某一项参数,使用过滤命令grep(linux), windows下可使用findstr来过滤
adb shell dumpsys media.camera |grep picture-size
//输出
picture-size: 3264x2448
picture-size-values:
5520x4140,5984x3366,3840x2160,3264x2448,2048x1536,1920x1080,1280x720,640x480,480x320,320x240
其中参数后面带values(比如上面picture-size-values)表示这个参数可设置的值,需要注意的是, 如果当前没有打开Camera, 执行命令也会有相应输出, 此时输出的值是默认值(打开camera不设置任何参数).
上面说的是针对HAL1的参数, 如果是HAL3参数, 输出内容就不一样了,如下:
adb shell dumpsys media.camera
// HAL3部分参数输出
android.control.aeLockAvailable (10024): byte[1]
[TRUE ]
android.control.awbLockAvailable (10025): byte[1]
[TRUE ]
android.control.availableModes (10026): byte[2]
[1 2 ]
android.shading.availableModes (100002): byte[2]
[1 2 ]
android.statistics.info.availableLensShadingMapModes (120007): byte[1]
[0 ]
android.sensor.info.preCorrectionActiveArraySize (f000a): int32[4]
可以看到, HAL1和HAL3差别很大, HAL3参数格式和Android Camera API2是对应的,如果要看HAL3的相关参数, 直接使用grep命令存在问题, 因为grep只会输出有关键字的那一行,但HAL3参数的Key和Values一般都不在一行, 所以要把输出保存到文件中,然后搜索关键字
adb shell dumpsys media.camera > camera.txt
No Display Mode
通常情况下,正常使用Camera的流程是:
- 打开Camera
- 设置参数
- 设置预览Surface
- 开启预览:startPreview()
- 拍照:takePicture()
但有些情况下, 我们希望不设置预览Surface就能拍照(默认情况下不设置预览进行拍照会Crash), 常见的应用场景是双摄项目, 打开了两个Camera,但副摄预览不需要用户看到, 这样做也可以减少系统资源占用, 高通和MTK平台都提供了不设置预览也可以拍照的功能, 使用方法如下:
- 高通平台
高通平台可以在ZSL开启的情况下, 通过设置参数Parameters.set("no-display-mode", "1");
来达到不用设置Surface也能拍照的效果(参数必须在startPreview()之前设置). - MTK平台
MTK平台要更简单些, 保证ZSD开启的情况下, 直接不用设置Surface, 进行startPreview()和takePicture()即可.
Camera内存占用问题
我们都知道, Camera App和CameraService是两个进程, App内存占用大家一般用Android Studio就能看到, 但如果我们在HAL层加入了一些修改, 要看内存占用, 这个时候就需要看CameraService的内存占用了, CameraService调用HAL层接口, 所以HAL层内存占用就体现在CameraService中, 要找到CameraService的进程, 可通过如下方法:
Android 7.1及以下版本
adb shell ps |grep -i camera
//高通Android 7.1.1, 输出如下:
cameraserver 371 1 20456 2240 binder_thr a84b396c S /system/bin/cameraserver
camera 401 1 73188 3404 poll_sched ab1a8a6c S /system/bin/mm-qcamera-daemon
可以看到和Camera相关的进程有两个, 一个是我们要找的CameraService,名称为cameraserver
另一个就高通的Camera守护进程qcamera-daemon
找到进程后, 查看内存就简单了,也是通过dumpsys来查看(meminfo后面也可以使用pid作为参数):
$ adb shell dumpsys meminfo cameraserver
Applications Memory Usage (in Kilobytes):
Uptime: 24402928 Realtime: 83208095
Pss Private Private Swap Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 196 196 0 40 0 0 0
Dalvik Heap 0 0 0 0 0 0 0
Stack 32 32 0 64
Other dev 5 0 4 0
.so mmap 470 204 176 508
Other mmap 14 8 4 8
Unknown 88 88 0 88
TOTAL 805 528 184 708 0 0 0
App Summary
Pss(KB)
------
Java Heap: 0
Native Heap: 196
Code: 380
Stack: 32
Graphics: 0
Private Other: 104
System: 93
TOTAL: 805 TOTAL SWAP (KB): 708
Android 8.0及以上版本
在Android 8.0上, Google推出了一项Project Treble计划,旨在规范HAL接口, 简化Android的版本升级,其中一个重要改变就是, Framework层和HAL层也是通过Binder(此Binder和我们通常使用Binder不是同一个)通信, 因此HAL层内存占用并不在CameraService中, 而是在名为android.hardware.camera.provider@xx
(xx表示版本号)中,比如SDM450平台就是[email protected]
,我们找到这个进程也是通过 adb shell ps |grep -i camera
, 然后通过pid(名字可能会重复)来查看内存即可, 基本方法和上面一样, 只是进程不是CameraService.
API和HAL版本
Camera由于其复杂的特点, Android在发展过程中, Camera API也进行了更新, 在Android 5.0上, Google推出了Camera2 API, 同样HAL版本中API1和API2对应的版本是HAL1和HAL3. 但由于一些原因, HAL3和API2普及并不理想, 到目前为止, 中低端手机基本都是API1+HAL1, 对于开发者来说,我们可能想知道一个App用的API和HAL的版本,这个时候可以通过过滤Log方式来实现(高通和MTK都通用):
$ adb logcat |grep CameraService
04-07 14:07:43.174 371 4415 I CameraService: CameraService::connect call (PID -1 "com.smewise.camera2", camera ID 0) for HAL version default and Camera API version 1
可以看到Log打印的信息有App API的版本和HAL版本, API版本只有API1 和API2, HAL版本一般有三个结果: 256, 768, default.
256代表使用HAL1, 768为HAL3, default一般表示调用的是没有特别指定版本, 根据平台配置来决定,这种情况如果想知道到底使用的哪个版本, 需要看平台代码或者一些Log.
注:如果平台只支持HAL1, App通过API2去调用Camera的话, Framework层会将API2的调用转为用API1去调用, 也就是说虽然App使用的API2, 单对于CameraService来说使用的还是API1.
高通平台副摄可见
做过双摄项目的一般都知道, 副摄(一般id为2)对上层App是不可见的, 但我们在开发过程用需要对副摄进行一些测试, 因此是需要能打开副摄的,高通平台隐藏副摄是在Framework层做的处理, 代码如下:
API1 代码:
frameworks/base/core/java/android/hardware/Camera.java
public static int getNumberOfCameras() {
boolean exposeAuxCamera = false;
String packageName = ActivityThread.currentOpPackageName();
/* Force to expose only two cameras
* if the package name does not falls in this bucket
*/
String packageList = SystemProperties.get("camera.aux.packagelist");
if (packageList.length() > 0) {
TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
splitter.setString(packageList);
for (String str : splitter) {
if (packageName.equals(str)) {
exposeAuxCamera = true;
break;
}
}
}
int numberOfCameras = _getNumberOfCameras();
if (exposeAuxCamera == false && (numberOfCameras > 2)) {
numberOfCameras = 2;
}
return numberOfCameras;
}
API2 代码:
frameworks/base/core/java/android/hardware/camera2/CameraManager.java
private ArrayList getOrCreateDeviceIdListLocked() throws CameraAccessException {
// 部分代码省略
try {
numCameras = cameraService.getNumberOfCameras(CAMERA_TYPE_ALL);
/* Force to expose only two cameras
* if the package name does not falls in this bucket
*/
boolean exposeAuxCamera = false;
String packageName = ActivityThread.currentOpPackageName();
String packageList = SystemProperties.get("camera.aux.packagelist");
if (packageList.length() > 0) {
TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
splitter.setString(packageList);
for (String str : splitter) {
if (packageName.equals(str)) {
exposeAuxCamera = true;
break;
}
}
}
if (exposeAuxCamera == false && (numCameras > 2)) {
numCameras = 2;
}
//部分代码省略
可以看到,这段代码逻辑是如果App包名在camera.aux.packagelist
这个属性中,则可以打开副摄, 否则不行.因此打开副摄有两种方法:
- 设置
camera.aux.packagelist
这个属性, 把要打开的App包名添加进去 - 删除这部分限制Camera个数的代码(不推荐)
SnapdragonCamera高级设置
高通平台调试Camera一般用的就是SnapdragonCamera这个App, 这个App默认的设置参数比较有限, 有些我们想调节的参数没有, 比如anti-banding, 其实高通埋了个彩蛋在设置中的, 只要疯狂点击设置菜单中的Reduce Red Eye(减少红眼)选项, 然后重新进入设置菜单, 就能看到额外的设置选项了, 内容非常多, 比较有用, 控制这个额外菜单选项(DeveloperMenu)的代码如下:
packages/apps/SnapdragonCamera/src/com/android/camera/PhotoMenu.java
public void onPreferenceClicked(ListPreference pref, int y) {
if (!mActivity.isDeveloperMenuEnabled()) {
if (pref.getKey().equals(CameraSettings.KEY_REDEYE_REDUCTION)) {
privateCounter++;
if (privateCounter >= DEVELOPER_MENU_TOUCH_COUNT) {
mActivity.enableDeveloperMenu();
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(mActivity);
prefs.edit().putBoolean(CameraSettings.KEY_DEVELOPER_MENU, true).apply();
RotateTextToast.makeText(mActivity,
"Camera developer option is enabled now", Toast.LENGTH_SHORT).show();
}
} else {
privateCounter = 0;
}
}
//其他代码省略
CameraService启动流程
CameraService是在开机的的时候启动的, 并且不管有没有App使用Camera, 进程会一直存在, 在开机时,CameraService会发起一次对底层Camera信息的查询, 这个操作主要是确定Camera的个数和Camera基本信息的, 也就是说如果在HAL层及以下, 修改了Camera个数, 需要重启手机才会生效.
另外由于CameraService开机启动, 如果HAL层修改东西存在问题, 可能会导致手机无法开机, 但据我所知, 高通平台Android 7.1.1及以后的版本, CameraService无法启动应该是不会影响整个系统的启动.