Android Service 跨进程启动失败
在项目中遇到了这样的需求:需要将部分功能设计在一个独立 app 中,运行时与调用的应用运行在不同的进程中,主要目的是为了防止调用 APP 随意获取服务 APP 中的资源。所以要设计两个 application,通过 IPC 通信方式,在两个 application 之间通信。
最直接的是 Service 方案,保证两者之间的通信,有数据往来,使用 bind 方式启动。
这篇文章记录唤醒 Service 失败的问题,并记录解决的方法。
启动 Service
启动一个 Service 的两种方式:
startService(Intent)/startForegroundService(Intent)bindService(Intent, ServiceConnection, int)
还有显示调用和隐式调用。
这里的场景是跨进程的调用,一个提供服务的 APP,一个使用服务的 APP,两者之间进行 IPC 通信。
先看看 Client 和 Server 端代码实现,很简单。
Client 端:
// Launch service.
private val _executor = ...
private val _servConn = object : ServiceConnection {
override fun onServiceConnected(
name: ComponentName, service: IBinder?
) {
println("onServiceConnected(ComponentName, IBinder?): Service_Process_Name=${name.packageName}, Type_Name=${name.className}, Short_Name=${name.shortClassName}")
println("onServiceConnected(ComponentName, IBinder?): Service=${service?.javaClass}, Is_Alive=${service?.isBinderAlive}, Service_Desc=${service?.interfaceDescriptor}")
}
override fun onServiceDisconnected(name: ComponentName) {
println("onServiceDisconnected(ComponentName): Unbind_Service_Component ---> Component_Package_Name=${name.packageName}, Type_Name=${name.className}")
}
}
val intent = Intent().apply {
// 1. 此方式,绑定的跨 app 服务启动失败。
setAction("com.sanren1024.action.OPEN_REMOTE")
setPackage("com.sanren1024.remoteservice")
}
context.bindService(
intent,
Context.BIND_AUTO_CREATE,
_executor,
_servConn
)
// manifest
<manifest>
<!-- Android 12+ 要设置 queries 标签 -->
<queries>
<package android:name="com.sanren1024.remoteservice" />
</queries>
<!-- Android 11+ 必须添加包可见性声,在服务 app 中声明的权限。 -->
<uses-permission android:name="com.sanren1024.permission.ACCESS_REMOTE" />
</manifest>
Server 端:
// Service
class VaultService : Service() {
override fun onCreate() {
super.onCreate()
println("VaultService#onCreate: Vault service created. PID=${Process.myPid()}, Process_Name=${getPrivateProcessName()}")
}
override fun onBind(intent: Intent): IBinder? {
println("VaultService#onBind(Intent): Vault service bound. Intent=$intent")
return null
}
/**
* 获取当前私有进程的进程名
*/
fun getPrivateProcessName(): String? {
val activityManager = getSystemService(ACTIVITY_SERVICE) as ActivityManager
val processes = activityManager.runningAppProcesses
val currentPid = Process.myPid()
if (processes != null) {
for (processInfo in processes) {
if (processInfo.pid == currentPid) {
return processInfo.processName
}
}
}
return "Unknown"
}
}
// manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<permission
android:name="com.sanren1024.permission.ACCESS_REMOTE"
android:protectionLevel="normal" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:allowBackup="true"
android:supportsRtl="true">
<service
android:name=".VaultService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="dataSync|connectedDevice|location"
android:permission="com.sanren1024.permission.ACCESS_REMOTE" >
<intent-filter>
<action android:name="com.sanren1024.action.OPEN_REMOTE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application>
</manifest>
程序很简单,但在测试过程中发现遇到了问题,在 Google Pixel 6 手机上,还有模拟器上都可以运行通过,正常启动 Service 程序,在 OPPO,一加手机上确是无法正常启动,情况看下。
测试设备
下面是测试的设备结果:
-
一加 ColorOS(Android 15):
isAllowStartFromStartService: prevent start com.sanren1024.remoteservice , intent = Intent { cmp=com.sanren1024.remoteservice/.RemoteService mCallingUid=10394 } by A系统阻止了跨进程启动 Service。
-
OPPO ColorOS(Android 13): 与上面的情况一致,被系统阻止。
-
Google Pixel6:
I ActivityManager: Start proc 17074:com.sanren1024.remoteservice/u0a668 for service {com.sanren1024.remoteservice/com.sanren1024.remoteservice.RemoteService} I System.out: RemoteService#onCreate: Remote service created. PID=17074, Process_Name=com.sanren1024.remoteservice I System.out: RemoteService#onStartCommand(Intent, Int, Int): Remote service started. Intent=Intent { cmp=com.sanren1024.remoteservice/.RemoteService }Pixel 6 启动 Service 是成功的。
从测试的情况看,OPPO,一加的系统中,阻止了跨进程启动 Service 的能力。
已测试品牌: OPPO,一加 —— “关联启动” 打开后,可以成功启动。
尚未测试品牌:小米,华为,VIVO
严格的限制
在 Google Android 原生系统的 pixel 6 手机上,bind 方式启动另一个服务 app 是成功的。但是在国产机的 OPPO,一加手机上测试下来是失败的,系统会阻止一个应用内普通 Service 被外部 application 唤起。从安全,电量消耗,防止滥用等方面考虑是可取的,但这也比原生的系统限制更加严格。对于确实需要设计成不同 UID 下 APP 的方案就不够友好。
要解决上面的问题,在经过一轮试错和 AI 问答后,得到了提示,很简单,与手机系统定制的 “关联启动” “自启动” 设置有关系。
- 在 OPPO,一加手机的 “设置 > 应用 > 关联启动” 中,找到提供服务 APP,打开 “关联启动” 开关,就可以执行上面的跨进程启动 Service。

1840

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



