Android 问题03_Service 跨进程启动失败

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

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,一加手机上确是无法正常启动,情况看下。

测试设备

下面是测试的设备结果:

  1. 一加 ColorOS(Android 15):

    isAllowStartFromStartService: prevent start com.sanren1024.remoteservice , intent = Intent { cmp=com.sanren1024.remoteservice/.RemoteService mCallingUid=10394 } by A
    

    系统阻止了跨进程启动 Service。

  2. OPPO ColorOS(Android 13): 与上面的情况一致,被系统阻止。

  3. 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。

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zen@sz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值