应用宝(MediaRouteProviderService)媒体路由保活思路研究

发布于:2025-07-31 ⋅ 阅读:(21) ⋅ 点赞:(0)

现象

gms通过android.media.MediaRouteProviderService拉活应用宝的现象

0

日志

07-29 18:00:55.625  1442  2258 D IntentFirewall: service:Block = false intent = Intent { act=android.media.MediaRouteProviderService cmp=com.tencent.android.qqdownloader/com.tencent.assistant.syscomponent.MediaRouteProviderService } callerUid = 10152 callerPid = 2330 callerPackage = com.google.android.gms calledPackage = com.tencent.android.qqdownloader calledUid = 1026507-29 18:00:56.207  1442  1496 D IntentFirewall: provider:Block = false intent is NULL!  callerUid = 10066 callerPid = -1 callerPackage = com.android.providers.blockednumber calledPackage = com.tencent.android.qqdownloader calledUid = 1026507-29 18:00:56.682  1442  2314 D IntentFirewall: provider:Block = false intent is NULL!  callerUid = 10265 callerPid = -1 callerPackage = com.tencent.android.qqdownloader calledPackage = com.tencent.android.qqdownloader calledUid = 1026507-29 18:00:56.717  1442  1918 D IntentFirewall: provider:Block = false intent is NULL!  callerUid = 10265 callerPid = -1 callerPackage = com.tencent.android.qqdownloader calledPackage = com.tencent.mm calledUid = 1028307-29 18:00:57.388  1442  2155 D IntentFirewall: service:Block = false intent = Intent { cmp=com.tencent.android.qqdownloader/com.tencent.assistant.daemon.CoreService } callerUid = 10265 callerPid = 5625 callerPackage = com.tencent.android.qqdownloader calledPackage = com.tencent.android.qqdownloader calledUid = 10265

系统自启动的拦截方案

方案1:动态拦截

原则是不影响兼容性或用户体验的条件下,对非可感知的后台启动行为可以进行拦截

判断启动方和被启动方的应用标签状态,如果都非可见且被启动方(应用宝)也没有被fork,但是由于caller是系统进程想要冷启动第三方App,还是需要慎重判断拦截(例如加一些灭屏条件或剔除gms类型拉起或Media路由非真实被使用等更精细化条件辅助判断是否拦截)

0

方案2:rule拦截

如下AOSP配置就行,可以简单包名也可以精确到called,caller,action和component,从配置文件我们也可以看出来,手机厂商拦截应用后台自启动,真的可以很随心所欲啊。第三方应用好不容易辛苦研究了一个保活技术,直接就被厂商配置文件给拦截了。这也就是后面app工程师,不怎么想研究保活技术的原因了哈。​​​​​​​

<service block="true" called="com.tencent.android.qqdownloader" caller="com.google.android.gms"    interaction="*" log="true">    <intent-filter>        <action name="android.media.MediaRouteProviderService" />    </intent-filter>    <component-filter        name="com.tencent.android.qqdownloader/com.tencent.assistant.syscomponent.MediaRouteProviderService" /></service><service block="true" called="com.tencent.android.qqdownloader" caller="*"    interaction="*" log="true">    <intent-filter>        <action name="android.media.MediaRoute2ProviderService" />    </intent-filter>    <component-filter        name="com.tencent.android.qqdownloader/com.tencent.assistant.syscomponent.MediaRouteProviderService2" /></service>

这种配置文件拦截的,可能是一些大厂的作风为主。因为这样不破坏整体拦截策略稳定性。即为了交付进行拦截

0

应用宝保活思路研究

应用宝通过以下策略增强保活效果:

高频绑定:媒体路由发现是周期性行为(如设备网络变化时),触发多次绑定。

多进程守护::live 进程与主进程分离,避免全量被杀。

系统白名单:媒体路由服务属于系统功能依赖,绑定请求通常不受后台限制

1. 应用宝如何使用SDK拉活自己呢

反编译一下应用宝原理就知道了哈

0

1. 清单文件注册(AndroidManifest.xml)

应用宝需在清单文件中声明自定义的 MediaRouteProviderService/MediaRoute2ProviderService,并注册系统标准的媒体路由 Action:

<service    android:name="com.tencent.assistant.syscomponent.MediaRoute2ProviderService"    android:label="@string/media_route_service_name"    android:process=":live" <!-- 使用独立进程保活 -->    android:exported="true"> <!-- 允许系统绑定 -->    <intent-filter>        <action android:name="android.media.MediaRouteProviderService" />    </intent-filter></service><service android:name="com.tencent.assistant.syscomponent.MediaRouteProviderService2" android:enabled="true" android:exported="true" android:process=":live">    <intent-filter>        <action android:name="android.media.MediaRoute2ProviderService"/>    </intent-filter></service>

保活关键点:

1.android.media.MediaRouteProviderService 是系统媒体路由器框架的固定 Action,用于发现服务

2.:live 进程后缀用于隔离并提高优先级,避免主进程被杀时连带失效

2. 服务实现类

应用宝其实也没做啥事情哈

package com.tencent.assistant.syscomponent;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.os.SystemClock;import androidx.annotation.Nullable;import com.tencent.assistant.Settings;import com.tencent.assistant.utils.SysComponentHelper;/* compiled from: ProGuard *//* loaded from: classes2.dex */public class MediaRouteProviderService2 extends BaseSysComponentService {    @Override // com.tencent.assistant.syscomponent.BaseSysComponentService    public SysComponentHelper.SysComponentType a() {        return SysComponentHelper.SysComponentType.MediaRouteProviderService2;    }    @Override // com.tencent.assistant.syscomponent.BaseSysComponentService, android.app.Service    @Nullable    public IBinder onBind(Intent intent) {        Settings.get().setAsync("key_live_media_service_on_bind", Long.valueOf(SystemClock.elapsedRealtime()));        SysComponentHelper.e(SysComponentHelper.SysComponentType.MediaRouteProviderService2, "service_bind");        return new Binder();    }    @Override // com.tencent.assistant.syscomponent.BaseSysComponentService, android.app.Service    public void onCreate() {        super.onCreate();    }}

Google Play 服务(com.google.android.gms)会主动绑定此服务以发现投屏设备,从而拉活应用宝进程。

2. 系统侧的冷启动堆栈

本质还是bindService哈,MediaRoute2ProviderServiceProxy的bindService冷启动了应用宝​​​​​​​

01-26 03:00:16.621  7461  7461 I ActivityManager: startProcessLocked com.tencent.android.qqdownloader:live01-26 03:00:16.621  7461  7461 I ActivityManager: java.lang.Throwable01-26 03:00:16.621  7461  7461 I ActivityManager:         at com.android.server.am.ProcessList.startProcessLocked(ProcessList.java:1697)01-26 03:00:16.621  7461  7461 I ActivityManager:         at com.android.server.am.ProcessList.startProcessLocked(ProcessList.java:2418)01-26 03:00:16.621  7461  7461 I ActivityManager:         at com.android.server.am.ProcessList.startProcessLocked(ProcessList.java:2558)01-26 03:00:16.621  7461  7461 I ActivityManager:         at com.android.server.am.ActivityManagerService.startProcessLocked(ActivityManagerService.java:2858)01-26 03:00:16.621  7461  7461 I ActivityManager:         at com.android.server.am.ActiveServices.bringUpServiceLocked(ActiveServices.java:4278)01-26 03:00:16.621  7461  7461 I ActivityManager:         at com.android.server.am.ActiveServices.bindServiceLocked(ActiveServices.java:2956)01-26 03:00:16.621  7461  7461 I ActivityManager:         at com.android.server.am.ActivityManagerService.bindServiceInstance(ActivityManagerService.java:12782)01-26 03:00:16.621  7461  7461 I ActivityManager:         at com.android.server.am.ActivityManagerService.bindServiceInstance(ActivityManagerService.java:12729)01-26 03:00:16.621  7461  7461 I ActivityManager:         at android.app.ContextImpl.bindServiceCommon(ContextImpl.java:2035)01-26 03:00:16.621  7461  7461 I ActivityManager:         at android.app.ContextImpl.bindServiceAsUser(ContextImpl.java:1974)01-26 03:00:16.621  7461  7461 I ActivityManager:         at com.android.server.media.MediaRoute2ProviderServiceProxy.bind(MediaRoute2ProviderServiceProxy.java:243)01-26 03:00:16.621  7461  7461 I ActivityManager:         at com.android.server.media.MediaRoute2ProviderServiceProxy.onBindingDied(MediaRoute2ProviderServiceProxy.java:312)01-26 03:00:16.621  7461  7461 I ActivityManager:         at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:2184)01-26 03:00:16.621  7461  7461 I ActivityManager:         at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:2221)

典型调用链:

MediaRouter2 → MediaRoute2ProviderServiceProxy.bind() → 目标服务进程启动 → getControllerName() 获取展示名称

​​​​​​​

/** * 媒体路由提供者服务的代理类,用于绑定和管理实际的 MediaRoute2ProviderService 服务。 * 通过 Binder 机制与系统媒体路由器框架交互,负责路由发现和会话控制。 *  * @see MediaRoute2ProviderService 系统媒体路由服务的基类 */final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {    // 系统定义的媒体路由服务接口 Action    public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService";    /**     * 绑定目标媒体路由提供者服务。     * 绑定成功后,通过 mServiceConnection 回调处理服务连接状态。     * 绑定优先级为前台服务(BIND_FOREGROUND_SERVICE),确保进程不被轻易回收。     *      * @throws SecurityException 若缺少绑定权限或目标服务未声明导出     */    private void bind() {        if (!mBound) {            if (DEBUG) {                Slog.d(TAG, this + ": Binding");            }            // 构建指向目标服务的 Intent,使用系统预定义 Action            Intent service = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE);            service.setComponent(mComponentName);            try {                // 绑定服务并设置自动创建和前台优先级标志                mBound = mContext.bindServiceAsUser(                        service,                        mServiceConnection,                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,                        new UserHandle(mUserId));                if (!mBound && DEBUG) {                    Slog.d(TAG, this + ": Bind failed");                }            } catch (SecurityException ex) {                // 捕获权限异常(如目标服务未声明 exported=true)                if (DEBUG) {                    Slog.d(TAG, this + ": Bind failed", ex);                }            }        }    }}/** * 媒体会话管理器,负责处理媒体控制器(MediaController)的元数据获取和展示逻辑。 * 包含从应用或服务中提取显示名称的功能。 */public class MediaSessions {    /**     * 获取媒体控制器的显示名称,优先从 MediaRouteProviderService 的服务标签中获取,     * 若未找到则回退到应用标签,最后返回包名。     *      * @param controller 目标媒体控制器实例     * @return 控制器友好名称(如“应用宝投屏”),默认返回包名     */    protected String getControllerName(MediaController controller) {        final PackageManager pm = mContext.getPackageManager();        final String pkg = controller.getPackageName();        try {            // 优先从 MediaRouteProviderService 的服务标签获取名称            if (USE_SERVICE_LABEL) {                // 查询所有注册了 MediaRouteProviderService 的服务                final List<ResolveInfo> ris = pm.queryIntentServices(                        new Intent("android.media.MediaRouteProviderService").setPackage(pkg), 0);                if (ris != null) {                    for (ResolveInfo ri : ris) {                        if (ri.serviceInfo == null) continue;                        // 确保服务属于当前包                        if (pkg.equals(ri.serviceInfo.packageName)) {                            final String serviceLabel =                                    Objects.toString(ri.serviceInfo.loadLabel(pm), "").trim();                            if (serviceLabel.length() > 0) {                                return serviceLabel; // 返回服务标签(如“应用宝投屏”)                            }                        }                    }                }            }            // 回退到应用标签            final ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);            final String appLabel = Objects.toString(ai.loadLabel(pm), "").trim();            if (appLabel.length() > 0) {                return appLabel; // 返回应用名称(如“应用宝”)            }        } catch (NameNotFoundException e) {            // 忽略异常,返回包名        }        return pkg; // 默认返回包名(如“com.tencent.android.qqdownloader”)    }}

MediaRoute2ProviderService:系统级服务,用于发布和管理媒体路由(如 Chromecast 设备),绑定后触发进程拉活。

进程保活:应用宝通过声明该服务并设置独立进程(:live),借助 Google Play 服务的绑定请求实现自启动

3. 那么媒体路由保活方案有用吗?

答:肯定是有用的,不然应用宝也不会搞这个保活技术。那为啥有些手机又没用呢?因为啊华米OV等一些大厂都有自己的自启动模块或功耗开发工程师啊,很多年前啊,他们在大数据中或用户反馈中发现了这一后台启动行为,进行了拦截处理哈。但是啊,我们手机其实不止华米OV,总有一些非主流的牌子,不一定有对应拦截媒体路由保活的策略哈。反正看着选择就行哈。但是其实应用后台保活的趋势不强了,尽量还是按需来就行了。能不能常驻后台运行,用户决定就行了。当然这块我们程序员决定不了,看公司商业良心了。


网站公告

今日签到

点亮在社区的每一天
去签到