一、基本信息
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就是谁。
public int sendInner(...) {
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,谁创建就具有谁的权限。
Alarm执行PendingIntent举例:
系统发送PendingIntent的场景有通知,Widget小程序以及Alarm闹钟等等。由于前两者都是用户主动触发,这里以AlarmManager为例测试:
Background activity launch blocked [
callingPackage: com.xxx.xxx;
callingUid: 10064; //创建者
appSwitchState: 1;
callingUidHasAnyVisibleWindow: false;
callingUidProcState: LAST_ACTIVITY;
isCallingUidPersistentSystemProcess: false;
balAllowedByPiSender: BackgroundStartPrivileges[allowsBackgroundActivityStarts=false, allowsBackgroundForegroundServiceStarts=false, originatingToken=null]; realCallingPackage: android.uid.system:1000[debugOnly];
realCallingUid: 1000; //执行者
realCallingUidHasAnyVisibleWindow: false; realCallingUidProcState: PERSISTENT; isRealCallingUidPersistentSystemProcess: true; originatingPendingIntent: PendingIntentRecord{7e7519a com.xxx.xxx startActivity}; backgroundStartPrivileges: BackgroundStartPrivileges[allowsBackgroundActivityStarts=false, allowsBackgroundForegroundServiceStarts=false, originatingToken=null]; intent: Intent { flg=0x10020004 hwFlg=0x100 cmp=com.xxx.xxx/.TargetActivity (has extras) }; callerApp: null; shouldAbortSelfLaunchWhenReturnHome: false; inVisibleTask: false]
被限制原因:
// false
final boolean balAllowedByPiSender = PendingIntentRecord.isPendingIntentBalAllowedByCaller(checkedOptions);
// 检查 options 的 KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED
private static boolean isPendingIntentBalAllowedByCaller(@Nullable Bundle 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.setPendingIntentBackgroundActivityLaunchAllowed(false);
...
}
之后通过PendingIntent#send将bundle参数带进去了
3)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。仅仅只有新增的内容可以加上去,修改是不可以的。