本文同步更新于旺仔的个人博客,访问可能有点慢,多刷新几次。

缘由

这几天想做一个点击跳转到TIM的扫一扫的Activity的功能,代码是这样的,就是普通的跳转

Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ComponentName component = new ComponentName("com.tencent.tim", "com.tencent.biz.qrcode.activity.ScannerActivity");
intent.setComponent(component);
intent.setAction("android.intent.action.VIEW");
try {startActivity(intent);
} catch (Exception e) {e.printStackTrace();
}

为什么我后面要加try/catch呢,因为不加的话会报异常,然后闪退,报的异常内容如下:

java.lang.SecurityException: Permission Denial:
starting Intent { act=android.intent.action.VIEW flg=0x10000000
cmp=com.tencent.tim/com.tencent.biz.qrcode.activity.ScannerActivity }
from ProcessRecord{e0031ac 25553:top.jowanxu.xposedtest/u0a175}
(pid=25553, uid=10175) not exported from uid 10151

Exported属性

wtf?没有权限?然后呢,百度了下,发现是Activity的属性exported的值为false,然后别的app就打不开这个Activity了,如果要打开的话,就必须和这个Activity在同一Application下或者uid相同才行。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"...android:sharedUserId="com.example.categorytest">...
</manifest>

同一不能打开的还有在没有设置exported属性的时候,也没有设置intent-filter属性的话,也是打不开这个Activity的。

<activity
android:name=".ScannerActivity"
android:label="@string/app_name"
android:exported="false"/> <!-- 设置了exported属性值为false --><!-- 如果Activity里面至少有一个filter的话,意味着这个Activity可以被其它应用从外部唤起,这个时候它的默认值是true -->
<activity
android:name=".SecondActivity"
android:label="@string/app_name"><intent-filter></intent-filter>
</activity>

然后我们Analyze APK一下我们的TIM的apk,打开它的AndroidManifest.xml文件,然后搜索ScannerActivity,发现ScannerActivity里面的exported的值果然是false。

既然如此的话,那就看一下当Activity的exported属性值为false的时候,为什么不能调起这个Activity,而且还会报异常。
startActivity的源码看起,既然我们一开始的问题是Permission Denial,那么我们查看的关键词就必须包含permission,这样看起源码来就方便许多。

源码

Activity类

首先是Activity里面的startActivity,发现他是调用自己的另一个同名不同参数的方法。

@Override
public void startActivity(Intent intent) {this.startActivity(intent, null);
}

跳到startActivity(Intent intent, @Nullable Bundle options)方法后,因为options参数为null,所以是调用startActivityForResult(@RequiresPermission Intent intent, int requestCode)这个方法。

@Override
public void startActivity(Intent intent, @Nullable Bundle options) {if (options != null) {startActivityForResult(intent, -1, options);} else {// Note we want to go through this call for compatibility with// applications that may have overridden the method.startActivityForResult(intent, -1);}
}

跳到startActivityForResult方法后,发现又是调用同名不同参数的方法startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options)

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) {// requestCode = -1startActivityForResult(intent, requestCode, null);
}

接着看mParent == null条件里面的代码,关键词startActivity,然后找到execStartActivity(),是Instrumentation类里面的方法。

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {if (mParent == null) {options = transferSpringboardActivityOptions(options);Instrumentation.ActivityResult ar =mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);if (ar != null) {mMainThread.sendActivityResult(mToken, mEmbeddedID, requestCode, ar.getResultCode(),ar.getResultData());}....} else {....}}

Instrumentation类

跳转到execStartActivity方法里,同样关键词startActivity,可以看到是ActivityManagerNative.getDefault().startActivity()方法和checkStartActivityResult()方法,我们先来看checkStartActivityResult()方法。

    public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, String target,Intent intent, int requestCode, Bundle options) {IApplicationThread whoThread = (IApplicationThread) contextThread;....try {intent.migrateExtraStreamToClipData();intent.prepareToLeaveProcess(who);int result = ActivityManagerNative.getDefault().startActivity(whoThread, who.getBasePackageName(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()),token, target, requestCode, 0, null, options);checkStartActivityResult(result, intent);} catch (RemoteException e) {throw new RuntimeException("Failure from system", e);}return null;}

点进去之后发现,这里面就是我们经常startActivity之后,在类没找到或者没有在AndroidManifest中注册等等之后会报出的异常的判断方法。

    public static void checkStartActivityResult(int res, Object intent) {if (res >= ActivityManager.START_SUCCESS) {return;}switch (res) {case ActivityManager.START_INTENT_NOT_RESOLVED:case ActivityManager.START_CLASS_NOT_FOUND:if (intent instanceof Intent && ((Intent)intent).getComponent() != null)throw new ActivityNotFoundException("Unable to find explicit activity class "+ ((Intent)intent).getComponent().toShortString()+ "; have you declared this activity in your AndroidManifest.xml?");throw new ActivityNotFoundException("No Activity found to handle " + intent);case ActivityManager.START_PERMISSION_DENIED:throw new SecurityException("Not allowed to start activity "+ intent);case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:throw new AndroidRuntimeException("FORWARD_RESULT_FLAG used while also requesting a result");case ActivityManager.START_NOT_ACTIVITY:throw new IllegalArgumentException("PendingIntent is not an activity");case ActivityManager.START_NOT_VOICE_COMPATIBLE:throw new SecurityException("Starting under voice control not allowed for: " + intent);case ActivityManager.START_VOICE_NOT_ACTIVE_SESSION:throw new IllegalStateException("Session calling startVoiceActivity does not match active session");case ActivityManager.START_VOICE_HIDDEN_SESSION:throw new IllegalStateException("Cannot start voice activity on a hidden session");case ActivityManager.START_CANCELED:throw new AndroidRuntimeException("Activity could not be started for "+ intent);default:throw new AndroidRuntimeException("Unknown error code "+ res + " when starting " + intent);}}

IActivityManager接口

点击startActivity()之后,跳转到IActivityManager接口里面来了,这个接口就是管理Activity的,然后我们从ActivityManagerNative.getDefault().startActivity()看出调用者是在ActivityManangerNative类里面。

/*** System private API for talking with the activity manager service.  This* provides calls from the application back to the activity manager.** {@hide}*/
public interface IActivityManager extends IInterface {public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flags,ProfilerInfo profilerInfo, Bundle options) throws RemoteException;....
}

ActivityManagerNative类

这时候到了ActivityManagerNative类里面,实现了IActivityManager接口,同时ActivityManagerNative还是一个抽象类,说明ActivityManagerNative.getDefault().startActivity()调用startActivity调用的对象是该类的子类。


public abstract class ActivityManagerNative extends Binder implements IActivityManager
{....static public IActivityManager getDefault() {return gDefault.get();}....
}

然后我们通过ctrl + shift + F打开搜索,关键词是extends ActivityManagerNative,scope选择custom,然后Find。

然后就找到了ActivityManagerService

ActivityManagerService类

ActivityManagerService类类是final类型,不能被继承,然后我们来看一下他的startActivity方法。


public final class ActivityManagerService extends ActivityManagerNativeimplements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {....@Overridepublic final int startActivity(IApplicationThread caller, String callingPackage,Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,resultWho, requestCode, startFlags, profilerInfo, bOptions,UserHandle.getCallingUserId());}
}

startActivity方法是调用了startActivityAsUser方法,我们继续走下去,来到了startActivityAsUser方法后发现,是调用了ActivityStarter类里面的startActivityMayWait方法。

@Overridepublic final int startActivityAsUser(IApplicationThread caller, String callingPackage,Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {enforceNotIsolatedCaller("startActivity");userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),userId, false, ALLOW_FULL_ONLY, "startActivity", null);// TODO: Switch to user app stacks here.return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,profilerInfo, null, null, bOptions, false, userId, null, null);}

ActivityStarter类

startActivityMayWait方法内容很多,挑重点看,关键词startActivity,同时看permission相关的有没有,然后我们找到了startActivityLocked方法。

    final int startActivityMayWait(IApplicationThread caller, int callingUid,String callingPackage, Intent intent, String resolvedType,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,IBinder resultTo, String resultWho, int requestCode, int startFlags,ProfilerInfo profilerInfo, IActivityManager.WaitResult outResult, Configuration config,Bundle bOptions, boolean ignoreTargetSecurity, int userId,IActivityContainer iContainer, TaskRecord inTask) {....ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);.... ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);....final ActivityRecord[] outRecord = new ActivityRecord[1];int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,aInfo, rInfo, voiceSession, voiceInteractor,resultTo, resultWho, requestCode, callingPid,callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,options, ignoreTargetSecurity, componentSpecified, outRecord, container,inTask);....return res;}}

继续,走到startActivityLocked方法里面,内容特别多,同样挑关键词startActivitypermission看,结果我们找到了mSupervisor.checkStartAnyActivityPermission方法和startActivityUnchecked方法,既然我们的目的是找跟permission相关的,那么我们就只看checkStartAnyActivityPermission方法内容吧。

    final int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,String callingPackage, int realCallingPid, int realCallingUid, int startFlags,ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,ActivityRecord[] outActivity, ActivityStackSupervisor.ActivityContainer container,TaskRecord inTask) {int err = ActivityManager.START_SUCCESS;....final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;....boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity, callerApp,resultRecord, resultStack, options);abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,callingPid, resolvedType, aInfo.applicationInfo);....try {mService.mWindowManager.deferSurfaceLayout();err = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags,true, options, inTask);} finally {mService.mWindowManager.continueSurfaceLayout();}postStartActivityUncheckedProcessing(r, err, stack.mStackId, mSourceRecord, mTargetStack);return err;}

ActivityStackSupervisor类

根据mSupervisor.checkStartAnyActivityPermission我们来到了ActivityStackSupervisor类的checkStartAnyActivityPermission方法,方法内容不长,直接往下看

    boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo,String resultWho, int requestCode, int callingPid, int callingUid,String callingPackage, boolean ignoreTargetSecurity, ProcessRecord callerApp,ActivityRecord resultRecord, ActivityStack resultStack, ActivityOptions options) {// 判断权限final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,callingUid);// 如果startAnyPerm的值为0,也就是PERMISSION_GRANTED的话,直接返回trueif (startAnyPerm ==  PERMISSION_GRANTED) {return true;}final int componentRestriction = getComponentRestrictionForCallingPackage(aInfo, callingPackage, callingPid, callingUid, ignoreTargetSecurity);final int actionRestriction = getActionRestrictionForCallingPackage(intent.getAction(), callingPackage, callingPid, callingUid);if (componentRestriction == ACTIVITY_RESTRICTION_PERMISSION|| actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {if (resultRecord != null) {resultStack.sendActivityResultLocked(-1,resultRecord, resultWho, requestCode,Activity.RESULT_CANCELED, null);}final String msg;// 重点就是这里了if (actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {msg = "Permission Denial: starting " + intent.toString()+ " from " + callerApp + " (pid=" + callingPid+ ", uid=" + callingUid + ")" + " with revoked permission "+ ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction());} else if (!aInfo.exported) {msg = "Permission Denial: starting " + intent.toString()+ " from " + callerApp + " (pid=" + callingPid+ ", uid=" + callingUid + ")"+ " not exported from uid " + aInfo.applicationInfo.uid;} else {msg = "Permission Denial: starting " + intent.toString()+ " from " + callerApp + " (pid=" + callingPid+ ", uid=" + callingUid + ")"+ " requires " + aInfo.permission;}Slog.w(TAG, msg);throw new SecurityException(msg);}if (actionRestriction == ACTIVITY_RESTRICTION_APPOP) {final String message = "Appop Denial: starting " + intent.toString()+ " from " + callerApp + " (pid=" + callingPid+ ", uid=" + callingUid + ")"+ " requires " + AppOpsManager.permissionToOp(ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction()));Slog.w(TAG, message);return false;} else if (componentRestriction == ACTIVITY_RESTRICTION_APPOP) {final String message = "Appop Denial: starting " + intent.toString()+ " from " + callerApp + " (pid=" + callingPid+ ", uid=" + callingUid + ")"+ " requires appop " + AppOpsManager.permissionToOp(aInfo.permission);Slog.w(TAG, message);return false;}if (options != null && options.getLaunchTaskId() != -1) {final int startInTaskPerm = mService.checkPermission(START_TASKS_FROM_RECENTS,callingPid, callingUid);if (startInTaskPerm != PERMISSION_GRANTED) {final String msg = "Permission Denial: starting " + intent.toString()+ " from " + callerApp + " (pid=" + callingPid+ ", uid=" + callingUid + ") with launchTaskId="+ options.getLaunchTaskId();Slog.w(TAG, msg);throw new SecurityException(msg);}}return true;}

找了那么久,终于找到了,开头提出的问题,就是下面这段代码里面的!aInfo.exported出现的。

if (actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {msg = "Permission Denial: starting " + intent.toString()+ " from " + callerApp + " (pid=" + callingPid+ ", uid=" + callingUid + ")" + " with revoked permission "+ ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction());
} else if (!aInfo.exported) {msg = "Permission Denial: starting " + intent.toString()+ " from " + callerApp + " (pid=" + callingPid+ ", uid=" + callingUid + ")"+ " not exported from uid " + aInfo.applicationInfo.uid;
} else {msg = "Permission Denial: starting " + intent.toString()+ " from " + callerApp + " (pid=" + callingPid+ ", uid=" + callingUid + ")"+ " requires " + aInfo.permission;
}
Slog.w(TAG, msg);
throw new SecurityException(msg);

aInfo的类型是ActivityInfo,里面有个exported的属性,就是我们在AndroidManifest.xml里面设置的值。

/*** Information you can retrieve about a particular application* activity or receiver. This corresponds to information collected* from the AndroidManifest.xml's <activity> and* <receiver> tags.*/
public class ActivityInfo extends ComponentInfoimplements Parcelable {
}

总结

翻了那么多的源码,看到这里我们的疑惑就解除了,这里报异常是还没进行到调起要跳转的Activity的时候就已经报SecurityException异常了,也就是在checkStartAnyActivityPermission方法里面报异常,没有再往下面走startActivityUnchecked这里面启动Activity的代码。

所以,在exported属性为false的时候,别人是调用不了这个Activity的,那么我的一开始的想法是实现不了的,那就只能通过命令行来调起Activity了,当然这操作是需要root的。

探究Exported属性对startActivity的影响相关推荐

  1. 结合源码谈谈Activity的exported属性

    Activity的exported属性在单个App可能用得比较少,但对于对外接口的Activity或公司内部多个应用间接口调用的设计会有比较大的影响.本文基于android 6.0.1的源码谈谈Act ...

  2. 深入理解 Android 组件的 exported 属性

    我们知道在AndroidManifest.xml文件中,四大组件都有android:exported属性,是个boolean值,可以为true或false.Activity的exported属性在单个 ...

  3. android:exported 属性详解

    昨天在用360扫描应用漏洞时,扫描结果,出来一个android:exported属性,其实之前根本不知道这个属性,更不知道这个属性用来干嘛的,详情见下图: 因此,查了官方API,学习了一下这个属性! ...

  4. Android开发-Activity中“android:exported“属性的作用,以及“Permission Denial: starting Intent“错误解决

    如何在一个应用程序中,启动另外一个应用程序?最近正有这样的需求,也踩了一个小坑.本节介绍使用Activity中"android:exported"属性来实现这种访问. Activi ...

  5. 关于Android应用本地拒绝服务漏洞和android:exported属性

    最近了项目中遇到一个Android应用本地拒绝服务漏洞的问题,第一眼看到这个玩意,我去,什么东东? 请教了下万能的度娘亲,才知道是应用本身组件对外性的一个问题.先简单说下这个漏洞. 下面漏洞介绍摘自阿 ...

  6. android默认exported_android:exported 属性详解

    转自http://blog.csdn.net/watermusicyes/article/details/46460347 昨天在用360扫描应用漏洞时,扫描结果,出来一个Android:export ...

  7. android默认exported_android:exported 属性详解-阿里云开发者社区

    昨天在用360扫描应用漏洞时,扫描结果,出来一个Android:exported属性,其实之前根本不知道这个属性,更不知道这个属性用来干嘛的,详情见下图: 因此,查了官方API,学习了一下这个属性! ...

  8. Matlab仿真炮弹飞行轨迹——探究射弹参数对飞行轨迹的影响

    目录 1.分析炮弹受力 2.设定参数并仿真 3.通过仿真寻找最佳射弹速度 3.1.射弹角度的影响 3.2.射弹速率的影响 3.3.炮弹属性和空气的影响 3.3.1.空气阻力系数的影响 3.3.2.炮弹 ...

  9. android默认exported_AndroidManifest.xml文件中exported属性解析

    4.目标Activity的属性Android:exported="true" 如果组件包含有intent-filter则 exported默认值为true; 没有intent-fi ...

最新文章

  1. 链表问题1——打印两个有序链表的公共部分
  2. linux下错误的捕获:errno、perror和strerror的使用
  3. php layui 框架,Thinkphp5+Layui高颜值内容管理框架
  4. vue-cli@2的原理解析
  5. 机器学习之线性代数总结
  6. Windows 8 页面应用测试(2)
  7. docker 服务器重启 镜像丢失_将你的前端应用打包成docker镜像并部署到服务器?仅需一个脚本搞定...
  8. ikbc机械键盘打字出现重复_入手第一把机械键盘,打字打到上瘾——ikbc 新Poker键盘 体验...
  9. react antd select默认选中第一项
  10. 无人机影像的植被覆盖度、叶面积指数估算
  11. RFM 客户价值分析
  12. IP前缀列表配置实验
  13. 重积分 | 第二类曲面积分投影法正负判断
  14. 青年是科学的未来:JGG诚聘青年编委
  15. Jupyter Notebook(Anaconda)【快捷键】
  16. 软件工程与计算II-19-软件测试
  17. zabbix是什么?主要用来做啥?
  18. MangoDB的重要概念
  19. java毕设:基于springboot的服装搭配推荐系统(springboot+layui+jq+html)1011
  20. java修改窗口形状_java – 使用透明图像的AWT自定义窗口形状

热门文章

  1. 批处理命令更新文件内容
  2. 70.各品牌笔记本xp2.xp3系统下载
  3. Buildroot 神器
  4. 大学平时分真的有用么?
  5. 单片机、嵌入式的大神都平时浏览什么网站?
  6. 关闭 iTunes 自动同步
  7. 扼腕:东芝宣布放弃HD-DVD格式
  8. “华为杯”研究生数学建模竞赛2020年-【华为杯】E题:能见度估计与预测(附获奖论文)
  9. git图标不显示的解决方案(亲测有效)
  10. vue实现课表,不同课程颜色不同