Android 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就是谁。
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。仅仅只有新增的内容可以加上去,修改是不可以的。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容