乐于分享
好东西不私藏

PendingIntent源码分析

PendingIntent源码分析

一、基本信息

PendingIntent对Intent进行包装,可独立存在,由其他应用或系统触发。PendingIntent 有创建者和执行者,执行者在执行的时候有创建者的权限。

二、源码分析

1)以Activity组件为例,简单了解下构建和发送的执行时序

构建和发送的执行时序

  • PendingIntent.getActivity(...) 返回的是PendingIntentRecord,它是IIntentSender的Stub端,它持有目标Intent;
  • PendingIntent内获取的IntentSender 是IIntentSender.Stub.asInterface(target),它是IIntentSender的proxy端;
  • PendingIntent.send和PendingIntent.getIntentSender().sendIntent是一样
    的效果,即通过proxy发起Activity启动。

    2)PendingIntent执行身份

    Key封装Intent相关信息:

    • packageName: PendingIntent创建者包名;
    • requestIntent: PendingIntent创建的组件启动Intent;

    PendingIntentRecord.sendInner发送执行:

    • callingUid: 对应PendingIntent属性uid, 赋值对应创建者uid;
    • realCallingUid: 对应PendingIntentRecord内部函数获取的Binder.getCallingUid(),即:谁触发了send,realCallingUid就是谁。
    publicintsendInner(...){final int realCallingUid = Binder.getCallingUid();final int realCallingPid = Binder.getCallingPid();...controller.mAtmInternal.startActivitiesInPackage  uid, // callingUid 构建Intent所在应用  realCallingPid,  realCallingUid, //realCallingUid 执行Intent所在的应用  key.packageName, key.featureId,allIntents, allResolvedTypes, resultTo, mergedOptions, userId,false /* validateIncomingUser */,this /* originatingPendingIntent */,mAllowBgActivityStartsForActivitySender.contains(allowlistToken));}  
    总结:PendingIntent的创建者:callingUid, 执行者:realCallingUid,谁创建就具有谁的权限。
    3)Activity组件后台启动限制点分析

    Alarm执行PendingIntent举例:系统发送PendingIntent的场景有通知,Widget小程序以及Alarm闹钟等等。由于前两者都是用户主动触发,这里以AlarmManager为例测试:

    Background activity launch blocked [callingPackage: com.xxx.xxxcallingUid10064//创建者appSwitchState1callingUidHasAnyVisibleWindowfalsecallingUidProcStateLAST_ACTIVITYisCallingUidPersistentSystemProcessfalsebalAllowedByPiSenderBackgroundStartPrivileges[allowsBackgroundActivityStarts=false, allowsBackgroundForegroundServiceStarts=false, originatingToken=null]; realCallingPackage: android.uid.system:1000[debugOnly]; realCallingUid1000//执行者realCallingUidHasAnyVisibleWindowfalserealCallingUidProcStatePERSISTENTisRealCallingUidPersistentSystemProcesstrueoriginatingPendingIntentPendingIntentRecord{7e7519a com.xxx.xxx startActivity}; backgroundStartPrivilegesBackgroundStartPrivileges[allowsBackgroundActivityStarts=false, allowsBackgroundForegroundServiceStarts=false, originatingToken=null]; intentIntent { flg=0x10020004 hwFlg=0x100 cmp=com.xxx.xxx/.TargetActivity (has extras) }; callerAppnullshouldAbortSelfLaunchWhenReturnHomefalseinVisibleTaskfalse]

    被限制原因:

    // falsefinal boolean balAllowedByPiSender = PendingIntentRecord.isPendingIntentBalAllowedByCaller(checkedOptions);// 检查 options 的 KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWEDprivate static boolean isPendingIntentBalAllowedByCaller(@NullableBundle options) {        if (options == null) {            return ActivityOptions.PENDING_INTENT_BAL_ALLOWED_DEFAULT;        }        return options.getBoolean(ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED,                ActivityOptions.PENDING_INTENT_BAL_ALLOWED_DEFAULT);    } 

    追溯该属性赋值点:

    AlarmHandler内部handleMessage:
    case ALARM_EVENT: {       // Disallow AlarmManager to start random background activity.      final Bundle bundle = getAlarmOperationBundle(alarm);      alarm.operation.send(/* context */ null/* code */0/* intent */null/* onFinished */null/* handler */ null/* requiredPermission */ null, bundle);  }
    而getAlarmOperationBundle最终执行:
    AlarmManagerService#onStart{   ...   // mActivityOptsRestrictBal 是 ActivityOptions    mActivityOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false);   // mBroadcastOptsRestrictBal 是 BroadcastOptions   mBroadcastOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false);   ...}

    之后通过PendingIntent#send将bundle参数带进去了。

    总结:Alarm针对后台启动activity和broadcast等组件启动限制场景都加了BAL属性开关,你反推置为true的点, 或许能发现一些不受限的特殊场景。

    4)PendingIntent的修改

    flag: PendingIntent.FLAG_MUTABLE(可变) 和 PendingIntent.FLAG_IMMUTABLE(不可变)pendingIntent的send(this, 0, modifyIntent); 作为入口com.android.server.am.PendingIntentRecord#sendInner

    public int sendInner(...) {final boolean immutable = (key.flags & PendingIntent.FLAG_IMMUTABLE) != 0;            if (!immutable) {                if (intent != null) {                    // 构建PendingIntent设置了对应的修改Intent内容的flag,就可以修改其对应内容。例如:                    // FILL_IN_ACTION、FILL_IN_PACKAGE、FILL_IN_COMPONENT、FILL_IN_DATA等                    int changes = finalIntent.fillIn(intent, key.flags);                    if ((changes & Intent.FILL_IN_DATA) == 0) {                        resolvedType = key.requestResolvedType;                    }                } else {                    resolvedType = key.requestResolvedType;                }                flagsMask &= ~Intent.IMMUTABLE_FLAGS;                flagsValues &= flagsMask;                finalIntent.setFlags((finalIntent.getFlags() & ~flagsMask) | flagsValues);            } 

    总结:在 Android 中,如果原始 PendingIntent 创建时未设置 Intent.FILL_IN_COMPONENT 标志,即使它是 FLAG_MUTABLE 的,也无法直接修改其底层 Intent 的 Component。仅仅只有新增的内容可以加上去,修改是不可以的。如果能获取系统创建的PendingIntent,且能其flag满足修改组件条件,会存在安全风险。