Android 11 后台启动service不能访问camera(摄像头)解决
Android 11增加了权限,在后台启动一个service,无法访问摄像头。
解决方案
解决方案分为两类:1.启动者有前台activity;2启动者本身是一个后台服务。
启动者有前台activity
在需要启动的服务里面增加前台服务的标签,注明需要使用camera。这个比较简单,直接贴代码。
<service
android:name="xxxxxxx"
android:foregroundServiceType="camera|location"
android:exported="true">
<intent-filter>
<action android:name="xxxxxxxx" />
</intent-filter>
</service>
启动者本身是一个后台服务
这个是本文解决的重点。需要说明的是,如果启动者所在的进程,有前台activity,那么属于第一种情况。本文主要是说的是,一个service,启动另外一个app的service来使用摄像头。为啥这么使用,先不管,我们只讨论怎么可以使用。
正常这样调用:app1 :service ->app 2:service->camera.
这里就算,app2 service,标注了第一种那样,系统也是不允许调用的。提示是:
Foreground service started from background can not havel ocation/camera/microphone access: service xxxxx
这个限制逻辑是在:ActiveServices里面。我们具体分析原因。
先看限制点:
if (!r.mAllowWhileInUsePermissionInFgs) {
Slog.w(TAG,
"Foreground service started from background can not have "
+ "location/camera/microphone access: service "
+ r.shortInstanceName);
}
主要就是这个属性mAllowWhileInUsePermissionInFgs,这个是在哪里赋值的呢?也是在这个类里面。
s.mAllowWhileInUsePermissionInFgs =
shouldAllowWhileInUsePermissionInFgsLocked(callingPackage,
Binder.getCallingPid(), Binder.getCallingUid(),
service, s, false);
针对赋值代码,找到解决方案。
private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage,
int callingPid, int callingUid, Intent intent, ServiceRecord r,
boolean allowBackgroundActivityStarts) {
// Is the background FGS start restriction turned on?
if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {//不会走
return true;
}
// Is the allow activity background start flag on?
if (allowBackgroundActivityStarts) {//传参就是false
return true;
}
boolean isCallerSystem = false;
final int callingAppId = UserHandle.getAppId(callingUid);
switch (callingAppId) {
case ROOT_UID:
case SYSTEM_UID:
case NFC_UID:
case SHELL_UID:
isCallerSystem = true;
break;
default:
isCallerSystem = false;
break;
}
if (isCallerSystem) {//我们也不是系统用户
return true;
}
if (r.app != null) {// // Whether the caller holds START_ACTIVITIES_FROM_BACKGROUND permission
ActiveInstrumentation instr = r.app.getActiveInstrumentation();
if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
return true;
}
}
final boolean hasAllowBackgroundActivityStartsToken = r.app != null
? !r.app.mAllowBackgroundActivityStartsTokens.isEmpty() : false;
if (hasAllowBackgroundActivityStartsToken) {
return true;
}
if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
== PERMISSION_GRANTED) {
return true;
}
// Is the calling UID at PROCESS_STATE_TOP or above?
final boolean isCallingUidTopApp = appIsTopLocked(callingUid);
if (isCallingUidTopApp) {
return true;
}
// Does the calling UID have any visible activity?
final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid);
if (isCallingUidVisible) {
return true;
}
final boolean isWhiteListedPackage =
mWhiteListAllowWhileInUsePermissionInFgs.contains(callingPackage);
if (isWhiteListedPackage) {//白名单不考虑
return true;
}
// Is the calling UID a device owner app?
final boolean isDeviceOwner = mAm.mInternal.isDeviceOwner(callingUid);
if (isDeviceOwner) {//设备拥有者,更加不考虑
return true;
}
return false;
}
第一步解决:增加START_ACTIVITIES_FROM_BACKGROUND
<uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
加进去,试下,你的app是否有?没有,继续往下看。
final boolean isCallingUidTopApp = appIsTopLocked(callingUid);
if (isCallingUidTopApp) {
return true;
}
app1 是否是top process。这里说下process state的定义:
@IntDef(flag = false, prefix = { "PROCESS_STATE_" }, value = {
PROCESS_STATE_UNKNOWN, // -1
PROCESS_STATE_PERSISTENT, // 0
PROCESS_STATE_PERSISTENT_UI,
PROCESS_STATE_TOP,
PROCESS_STATE_BOUND_TOP,
PROCESS_STATE_FOREGROUND_SERVICE,
PROCESS_STATE_BOUND_FOREGROUND_SERVICE,
PROCESS_STATE_IMPORTANT_FOREGROUND,
PROCESS_STATE_IMPORTANT_BACKGROUND,
PROCESS_STATE_TRANSIENT_BACKGROUND,
PROCESS_STATE_BACKUP,
PROCESS_STATE_SERVICE,
PROCESS_STATE_RECEIVER,
PROCESS_STATE_TOP_SLEEPING,
PROCESS_STATE_HEAVY_WEIGHT,
PROCESS_STATE_HOME,
PROCESS_STATE_LAST_ACTIVITY,
PROCESS_STATE_CACHED_ACTIVITY,
PROCESS_STATE_CACHED_ACTIVITY_CLIENT,
PROCESS_STATE_CACHED_RECENT,
PROCESS_STATE_CACHED_EMPTY,
})
PROCESS_STATE_TOP是2,前面几个,我们的app 基本达不到。
这个要做到,很简单,你是最前台的那个activity就是了。所以这一步,在我们假定的环境也不通。
PS。这里也可以看到,Android 进程优先级的控制。
继续往下:
// Does the calling UID have any visible activity?
final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid);
if (isCallingUidVisible) {
return true;
}
进去看代码,不要被注释误导,代码实现是在:ActivityTaskManagerService这里。
boolean isUidForeground(int uid) {
// A uid is considered to be foreground if it has a visible non-toast window.
return mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(uid);
}
看这句的注释,if it has a visible non-toast window。是不是就有想法了?
overlay dialog 来一把。
是不是就可以了?

7287

被折叠的 条评论
为什么被折叠?



