当前位置:   article > 正文

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

android:exported="true

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

Activity中"android:exported"属性说明:

在程序清单AndroidMenifest.xml文件中,可以设置这个属性。

Android中的Activity中"android:exported"属性设置为true,意味着允许让外部组件启动这个Activity;反之,则不允许让外部组件启动这个Activity;

如果设置了false,又在外部试图启动这个Activity,则会发生程序崩溃,报异常,例如:

java.lang.SecurityException: Permission Denial: starting Intent

入坑指南:

我要实现的功能是在App1中启动App2。在App1中,使用startActivity启动App2的activity,从而实现需求。只要将exported 设置为false,就入坑了。主要代码如下:

在app1中,

  1. //start other app
  2. Intent intent = mContext.getPackageManager().getLaunchIntentForPackage("com.test.app2");
  3. if (intent != null) {
  4. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  5. mContext.startActivity(intent);
  6. }

 这就会启动App2中的入口Activity。

在App2中,AndroidManifest.xml文件,

  1. <application android:icon="@drawable/icon"
  2. android:allowBackup="false"
  3. android:name=".MyApp2"
  4. android:label="@string/app_name"
  5. android:theme="@android:style/Theme.Light">
  6. <activity android:name="com.test.app2.activity.MainActivity"
  7. android:exported="true" //如果为false,就会发生异常
  8. android:finishOnTaskLaunch="false"
  9. android:launchMode="singleInstance"
  10. >
  11. </activity>
  12. ...
  13. />

 如果为exported 设置为false,就会发生异常,恭喜你,成功入坑。报错信息如下:

03-09 19:53:10.077 20340-20340/com.test.app1 D/AndroidRuntime: Shutting down VM
    
    --------- beginning of crash
03-09 19:53:10.078 20340-20340/com.test.app1 E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.test.app1, PID: 20340
    java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 pkg=com.test.app2 cmp=com.test.app2/com.test.app2.activity.MainActivity } from ProcessRecord{3d6aa2d 20340:com.test.app1/u0a83} (pid=20340, uid=10083) not exported from uid 10117
        at android.os.Parcel.readException(Parcel.java:1620)
        at android.os.Parcel.readException(Parcel.java:1573)
        at android.app.ActivityManagerProxy.startActivity(ActivityManagerNative.java:2658)
        at android.app.Instrumentation.execStartActivity(Instrumentation.java:1507)
        at android.app.Activity.startActivityForResult(Activity.java:3930)
        at android.app.Activity.startActivityForResult(Activity.java:3890)
        at android.app.Activity.startActivity(Activity.java:4213)
        at android.app.Activity.startActivity(Activity.java:4181)
        at com.test.app1.ui.DemoMainActivity.onClick(DemoMainActivity.java:70)
        at android.view.View.performClick(View.java:5204)
        at android.view.View$PerformClick.run(View.java:21153)
        at android.os.Handler.handleCallback(Handler.java:739)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:148)
        at android.app.ActivityThread.main(ActivityThread.java:5417)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

 当然,根据提示,也可以看出来,是“not exported”,所以,填坑也很容易:

android:exported="true"

源码阅读:

追根溯源,我们来看一下源码。搜索“not exported from uid” ,可以看到,有好几处都能搜到,而且代码文件看名字就能知道,是Android四大组件相关代码,如图:

 结合我们的错误信息提示,定位到错误提示的代码在ActivityStack.java的

startActivityLocked方法中:
  1. final int startAnyPerm = mService.checkPermission(
  2. START_ANY_ACTIVITY, callingPid, callingUid);
  3. final int componentPerm = mService.checkComponentPermission(aInfo.permission, callingPid,
  4. callingUid, aInfo.applicationInfo.uid, aInfo.exported);
  5. if (startAnyPerm != PERMISSION_GRANTED && componentPerm != PERMISSION_GRANTED) {
  6. String msg;
  7. if (!aInfo.exported) {
  8. msg = "Permission Denial: starting " + intent.toString()
  9. + " from " + callerApp + " (pid=" + callingPid
  10. + ", uid=" + callingUid + ")"
  11. + " not exported from uid " + aInfo.applicationInfo.uid;
  12. } else {
  13. msg = "Permission Denial: starting " + intent.toString()
  14. + " from " + callerApp + " (pid=" + callingPid
  15. + ", uid=" + callingUid + ")"
  16. + " requires " + aInfo.permission;
  17. }
  18. Slog.w(TAG, msg);
  19. throw new SecurityException(msg);
  20. }

哦,原来是在startActivityLocked中抛出的异常。还记得activity的启动流程么,一系列的startActivity相关函数的调用。

通过调用mService.checkPermission 和 mService.checkComponentPermission 进行获取权限。再根据返回值来判断是否有权限,如果没有权限,就会

throw new SecurityException(msg);

这个msg,就是上面我们提到的错误信息。

那么,mService是谁呢?答案就是大名鼎鼎的AMS(ActivityManagerService)的实例。

在check权限的时候,会进行进程id,用户id,是否是同一个应用,是否是系统用户以及exported是否为true......等等条件的判断。

我们直接来看到底是谁去check了exported的。答案是:ActivityManager。调用链:

  1. ActivityStack.startActivityLocked  -->
  2.     ActivityManagerService.checkComponentPermission -->
  3.         ActivityManager.checkComponentPermission    -->

ActivityManager.checkComponentPermission的代码:

  1. /** @hide */
  2. public static int checkComponentPermission(String permission, int uid,
  3. int owningUid, boolean exported) {
  4. // Root, system server get to do everything.
  5. if (uid == 0 || uid == Process.SYSTEM_UID) {
  6. return PackageManager.PERMISSION_GRANTED;
  7. }
  8. // Isolated processes don't get any permissions.
  9. if (UserId.isIsolated(uid)) {
  10. return PackageManager.PERMISSION_DENIED;
  11. }
  12. // If there is a uid that owns whatever is being accessed, it has
  13. // blanket access to it regardless of the permissions it requires.
  14. if (owningUid >= 0 && UserId.isSameApp(uid, owningUid)) {
  15. return PackageManager.PERMISSION_GRANTED;
  16. }
  17. // If the target is not exported, then nobody else can get to it.
  18. if (!exported) {
  19. Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid);
  20. return PackageManager.PERMISSION_DENIED;
  21. }
  22. if (permission == null) {
  23. return PackageManager.PERMISSION_GRANTED;
  24. }
  25. try {
  26. return AppGlobals.getPackageManager()
  27. .checkUidPermission(permission, uid);
  28. } catch (RemoteException e) {
  29. // Should never happen, but if it does... deny!
  30. Slog.e(TAG, "PackageManager is dead?!?", e);
  31. }
  32. return PackageManager.PERMISSION_DENIED;
  33. }

 可以看到,

// If the target is not exported, then nobody else can get to it.
        if (!exported) {
            Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid);
            return PackageManager.PERMISSION_DENIED;
        }

在这里进行了exported的判断,如果exported为false,就会

return PackageManager.PERMISSION_DENIED;

终于,知道了这个异常是怎么被throw出来的了。


android:exported="true" 和 android:exported="false",弄明白了么?

---------------------
作者:liranke
来源:CSDN
原文:https://blog.csdn.net/liranke/article/details/123437721
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/900581
推荐阅读
相关标签
  

闽ICP备14008679号