在android-12,当app切到后台,(大概)一分钟后,app正运行的service会被杀死;如果正播放摄像头,摄像头会被停掉;如果正在录音,录音会被停掉。当然,一分钟后,可能还有其它模块也被关了。本文只说通过修改aosp源码如何关掉这三种限制,其它模块可以类推。
可以确定,引发停止摄像头和停止录音是同一个原因,但不清楚和杀死service是不是同一个来源。至于是不是准时的一分钟,杀死service来自ActivityManagerConstants.DEFAULT_BACKGROUND_SETTLE_TIME,值是“60*1000”,即1分钟。停止摄像头、录音则不确定,实测下来大概一分钟。
一、关掉杀死service
app进入后台,一分钟后杀死service,logcat会输出类似以下日志。
Stopping service due to app idle: u0a77 -2m30s208ms com.kos.launcher/org.libsdl.app.ForOnDestroyService
“探讨8.0版本下后台service存活机制及保活”,这篇文章详细叙述了一分钟出处,以及android如何判断何样service会被杀死。虽然是andorid-8.0,到12时,一些逻辑变了,一些函数名变了,像getAppStartModeLocked变成getAppStartModeLOSP,但大致逻辑没变。
app被切到后台,经过一分钟后,ActiveServices会调用该app的stopInBackgroundLocked,参数uid是该app的uid。
<aosp>/frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
------
public final class ActiveServices {
void stopInBackgroundLocked(int uid) {
// Stop all services associated with this uid due to it going to the background
// stopped state.
ServiceMap services = mServiceMap.get(UserHandle.getUserId(uid));
ArrayList<ServiceRecord> stopping = null;
if (services != null) {
for (int i = services.mServicesByInstanceName.size() - 1; i >= 0; i--) {
ServiceRecord service = services.mServicesByInstanceName.valueAt(i);
if (service.appInfo.uid == uid && service.startRequested) {
if (mAm.getAppStartModeLOSP(service.appInfo.uid, service.packageName,
service.appInfo.targetSdkVersion, -1, false, false, false)
!= ActivityManager.APP_START_MODE_NORMAL) {
if (stopping == null) {
stopping = new ArrayList<>();
}
......此个service满足停止条件,加入stopping列表
stopping.add(service);
}
}
}
if (stopping != null) {
final int size = stopping.size();
// 停止stopping列表中那些个service
for (int i = size - 1; i >= 0; i--) {
ServiceRecord service = stopping.get(i);
service.delayed = false;
services.ensureNotStartingBackgroundLocked(service);
stopServiceLocked(service, true);
}
if (size > 0) {
mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
}
}
}
}
}stopInBackgroundLocked首先遍历service,经过一些条件判断,将满足条件的service放入stopping列表中,然后遍历这个列表停止service。在判断的条件中,一个是mAm.getAppStartModeLOSP方法返回值不能是ActivityManager.APP_START_MODE_NORMAL。于是要阻止放入stopping,自然想到了让getAppStartModeLOSP返回APP_START_MODE_NORMAL。
<aosp>/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
------
@GuardedBy(anyOf = {"this", "mProcLock"})
int getAppStartModeLOSP(int uid, String packageName, int packageTargetSdk,
int callingPid, boolean alwaysRestrict, boolean disabledOnly, boolean forcedStandby) {
if (mInternal.isPendingTopUid(uid)) {
return ActivityManager.APP_START_MODE_NORMAL;
}
// 增加下面这个if判断,希望关闭限制的apk直接返回APP_START_MODE_NORMAL。
if (packageName != null && packageName.equals("com.kos.launcher")) {
Slog.d(TAG, "{leagor}checkAllowBackground: uid=" + uid + " pkg=" + packageName + " it is in whitelist");
return ActivityManager.APP_START_MODE_NORMAL;
}
.....
}上面例代码中,com.kos.launcher是需要关闭限制的apk,原理很简单,增加个if判断,希望关闭限制的apk直接返回APP_START_MODE_NORMAL。
二、关掉停止摄像头
app进入后台,大概一分钟停止摄像头,aosp方面没有明显的logcat输出,如果app基于webrtc,会输出类似以下日志。
E/org.webrtc.Logging: Camera2Session: Error: Camera device could not be opened due to a device policy.
在webrtc源码搜“Camera device could not be opened due to a device policy”,它对应的是错误码CameraDevice.StateCallback.ERROR_CAMERA_DISABLED
<webrtc>/Camera2Session.java
------
private class CameraStateCallback extends CameraDevice.StateCallback {
private String getErrorDescription(int errorCode) {
switch (errorCode) {
......
case CameraDevice.StateCallback.ERROR_CAMERA_DISABLED:
return "Camera device could not be opened due to a device policy.";
......
}
}“Android 9.0 对后台程序使用Mic、Camera做了限制?”,这篇文章有在说这个问题。按那写的,除摄像头外,卖克风也有类似限制。
不同于杀死service是java代码,停止摄像头是C++。
app切到后台,大致一分钟后,会调用binder本地对象的BnUidObserver::onTransact,code参数值是ON_UID_IDLE_TRANSACTION。
<aosp>/frameworks/native/libs/binder/IUidObserver.cpp
------
status_t BnUidObserver::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case ON_UID_GONE_TRANSACTION: {
......
return NO_ERROR;
} break;
case ON_UID_ACTIVE_TRANSACTION: {
// app切回前台,会进入这里
CHECK_INTERFACE(IUidObserver, data, reply);
uid_t uid = data.readInt32();
onUidActive(uid);
return NO_ERROR;
} break;
case ON_UID_IDLE_TRANSACTION: {
// app切到后台,大概一分钟后,会进入这里
CHECK_INTERFACE(IUidObserver, data, reply);
uid_t uid = data.readInt32();
bool disabled = data.readInt32() == 1;
onUidIdle(uid, disabled);
return NO_ERROR;
} break;
case ON_UID_STATE_CHANGED_TRANSACTION: {
......
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}收到ON_UID_IDLE_TRANSACTION后,那些个从BnUidObserver派生的binder服务,会调用onUidIdle方法,当中一个就是UidPolicy
class CameraService :
public BinderService<CameraService>,
public virtual ::android::hardware::BnCameraService,
public virtual IBinder::DeathRecipient,
public virtual CameraProviderManager::StatusListener
{
class UidPolicy : public BnUidObserver, public virtual IBinder::DeathRecipient {
public:
......
void onUidActive(uid_t uid);
void onUidIdle(uid_t uid, bool disabled);
......
};
};
<aosp>/frameworks/av/services/camera/libcameraservice/CameraService.cpp
------
void CameraService::UidPolicy::onUidActive(uid_t uid) {
Mutex::Autolock _l(mUidLock);
mActiveUids.insert(uid);
}
void CameraService::UidPolicy::onUidIdle(uid_t uid, bool /* disabled */) {
// 官方代码没有下面5行, 是我加的,目的是关掉停止摄像头。
// 一开始理解时忽略这5行。
bool enable_background = true;
if (enable_background) {
ALOGI("{leagor}[UidPolicy::onUidIdle]uid: %i, enable background, do nothing", (int)uid);
return;
}
bool deleted = false;
{
Mutex::Autolock _l(mUidLock);
if (mActiveUids.erase(uid) > 0) {
// 要是正常的话,mActiveUids会包含uid,会进这个入口
deleted = true;
}
}
if (deleted) {
sp<CameraService> service = mService.promote();
if (service != nullptr) {
service->blockClientsForUid(uid);
}
}
}要是uid对应的app正在使用摄像头,那么mActiveUids会包含这个uid。onUidIdle调用service->blockClientsForUid(uid),后者会调用basicClient->block。
<aosp>/frameworks/av/services/camera/libcameraservice/CameraService.cpp
------
void CameraService::BasicClient::block() {
ATRACE_CALL();
// Reset the client PID to allow server-initiated disconnect,
// and to prevent further calls by client.
mClientPid = CameraThreadState::getCallingPid();
CaptureResultExtras resultExtras; // a dummy result (invalid)
notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DISABLED, resultExtras);
disconnect();
}在block(),通过notifyError报告错误:ERROR_CAMERA_DISABLED(3),也就是上面webrtc报出的错误码,然后断开。
要关掉停止摄像头,修改方法就是上面已写的,在UidPolicy::onUidIdle,什么都不做,直接返回。我的理解,mActiveUids存储着正在播放摄像头的uid,既然不停止播放,也就不再“mActiveUids.erase(uid)”删除了。顺便说下ON_UID_ACTIVE_TRANSACTION和onUidActive。app切换回前台后,那些个BnUidObserver会收到code值ON_UID_ACTIVE_TRANSACTION,然后调用onUidActive方法。UidPolicy::onUidActive会执行“mActiveUids.insert(uid)”,经过上面修改后,uid没被从mActiveUids删除,这里的insert会返回false。
在CameraService,除onUidIdle外,还有其它地方在调用basicClient->block(),不清楚它们使用场景。
三、关掉停止录音
<aosp>/frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp
------
void AudioPolicyService::UidPolicy::onUidIdle(uid_t uid, __unused bool disabled) {
// 官方代码没有下面5行, 是我加的,目的是关掉停止录音。
bool enable_background = true;
if (enable_background) {
// The output is a bit much, remark it
ALOGI("{leagor}[AudioPolicyService::UidPolicy::onUidIdle]uid: %i, enable background, do nothing", (int)uid);
return;
}
updateUid(&mCachedUids, uid, false, ActivityManager::PROCESS_STATE_UNKNOWN, true);
}“Android P系统禁止闲置APP在后台使用麦克风的问题”有描述AudioPolicyService大致逻逻。