Android 双开沙箱 VirtualApp 源码分析(五)BroadcastReceiver

在Android中实现Hook机制的实验 写过windows编辑的同学肯定对当时的特别强大的HOOK记忆犹新,不管是什么系统事件,都能捕捉住,那么在android中,如何实现Hook机制呢。 hook,又叫钩子,通常是指对一些方法进行拦截。这样当这些方法被调用时,也能够执行我们自己的代码,这也是面向切面编程的思想(AOP) android中,本身并不提供这样的拦截机制,但是有时候,我们可以在一些特殊的场合实现一种的Hook方法。 大致 阅读详情

Android 双开沙箱 VirtualApp 源码分析(五)BroadcastReceiver

方案猜测

同 Activity 一样,Client App 在 Menifest 中注册的静态广播外部 AMS 是无法知晓的,经过前几章的分析,相信大家已经是老司机了,我们可以先尝试提出自己的观点。

  1. 和 Activity 一样使用 Stub 组件占坑?仔细想一想是无法实现的,因为你无法预先确定 Client App 中广播的 Intent Filter。
  2. 使用动态注册即 context.registerBroadcastReceiver 代替静态注册,这确实是个好主意,但是重点在于注册的时机。我们需要在 VAService 启动时就预先把 VA 中所有安装的 Client App 的静态 Receiver 提前注册好,事实上外部 AMS 也是这么做的。不然的话,没有打开的 App 就无法收到广播了。

VA 静态广播注册

从前面我们知道,VAService 的启动时机实在 BinderProvider.onCreate():

@Override
    public boolean onCreate() {
        .....................
        VAppManagerService.get().scanApps();
        .....................
        return true;
    }

看到 VAppManagerService.get().scanApps()————>PersistenceLayer.read()——————->PackagePersistenceLayer.readPersistenceData()——————>VAppManagerService.loadPackage()———->VAppManagerService.loadPackageInnerLocked()————–>BroadcastSystem.startUp();

// 静态 Receiver 的注册
    public void startApp(VPackage p) {
        PackageSetting setting = (PackageSetting) p.mExtras;
        // 遍历 Client App 的 Receiver
        for (VPackage.ActivityComponent receiver : p.receivers) {
            ActivityInfo info = receiver.info;
            // 得到对应 Client App 在 VAService 中的记录列表
            List<BroadcastReceiver> receivers = mReceivers.get(p.packageName);
            if (receivers == null) {
                receivers = new ArrayList<>();
                mReceivers.put(p.packageName, receivers);
            }
            // 注册显式意图
            String componentAction = String.format("_VA_%s_%s", info.packageName, info.name);
            IntentFilter componentFilter = new IntentFilter(componentAction);
            BroadcastReceiver r = new StaticBroadcastReceiver(setting.appId, info, componentFilter);
            mContext.registerReceiver(r, componentFilter, null, mScheduler);
            // 推入记录
            receivers.add(r);
            // 遍历注册隐式意图
            for (VPackage.ActivityIntentInfo ci : receiver.intents) {
                IntentFilter cloneFilter = new IntentFilter(ci.filter);
                SpecialComponentList.protectIntentFilter(cloneFilter);
                r = new StaticBroadcastReceiver(setting.appId, info, cloneFilter);
                mContext.registerReceiver(r, cloneFilter, null, mScheduler);
                // 推入记录
                receivers.add(r);
            }
        }
    }

这里对每个 Client App 静态 Receiver 的信息使用统一的代理 StaticBroadcastReceiver 注册。

  1. 首先注册 Receiver 的显式意图,每个显式意图被重定向成格式为 “_VA_PKGNAME_CLASSNAME”的 componentAction 这么做的理由实际是真正注册是 VAService 进程空间的 StaticBroadcastReceiver 代理 Receiver,而不是 VA Client App 进程空间,所以直接注册 VA Client App 中的真实类名是没有意义的,这样通过 VAService 代理然后再从 Intent 中取出的 “_VA_PKGNAME_CLASSNAME”到 VA Client 中找到真正的 Receiver,这个逻辑和 Activity 的处理有些相似。
  2. 然后就是遍历 Intent-Filter,每个 Intent-Filter 注册一个 StaticBroadcastReceiver 代理。

这样我们的代理 Receiver 注册完毕了。

下面当代理 Receiver 收到广播时:

 @Override
        public void onReceive(Context context, Intent intent) {
            if (mApp.isBooting()) {
                return;
            }
            if ((intent.getFlags() & FLAG_RECEIVER_REGISTERED_ONLY) != 0 || isInitialStickyBroadcast()) {
                return;
            }
            String privilegePkg = intent.getStringExtra("_VA_|_privilege_pkg_");
            if (privilegePkg != null && !info.packageName.equals(privilegePkg)) {
                return;
            }
            PendingResult result = goAsync();
            if (!mAMS.handleStaticBroadcast(appId, info, intent, new PendingResultData(result))) {
                result.finish();
            }
        }

然后看到 handleStaticBroadcast

boolean handleStaticBroadcast(int appId, ActivityInfo info, Intent intent,
                                  PendingResultData result) {
        // 这里是取出真正的目标 Intent
        Intent realIntent = intent.getParcelableExtra("_VA_|_intent_");
        // 取出真正的目标 component
        ComponentName component = intent.getParcelableExtra("_VA_|_component_");
        // 用户 id
        int userId = intent.getIntExtra("_VA_|_user_id_", VUserHandle.USER_NULL);
        if (realIntent == null) {
            return false;
        }
        if (userId < 0) {
            VLog.w(TAG, "Sent a broadcast without userId " + realIntent);
            return false;
        }
        int vuid = VUserHandle.getUid(userId, appId);
        return handleUserBroadcast(vuid, info, component, realIntent, result);
    }

注意这里取出了真正的 Intent,和 Activity 类似,但是和 Activity 处理不同的是现在的逻辑还在 VAService 中:

然后 handleUserBroadcast()———–>handleStaticBroadcastAsUser()————>performScheduleReceiver():

private void performScheduleReceiver(IVClient client, int vuid, ActivityInfo info, Intent intent,
                                         PendingResultData result) {

        ComponentName componentName = ComponentUtils.toComponentName(info);
        BroadcastSystem.get().broadcastSent(vuid, info, result);
        try {
            // 远程调用 client app 的 scheduleReceiver
            client.scheduleReceiver(info.processName, componentName, intent, result);
        } catch (Throwable e) {
            if (result != null) {
                result.finish();
            }
        }
    }

client.scheduleReceiver() 这时候远程调用了 Client App 的 scheduleReceiver。这样我们回到了 Client App 进程空间:

@Override
    public void scheduleReceiver(String processName, ComponentName component, Intent intent, PendingResultData resultData) {
        ReceiverData receiverData = new ReceiverData();
        receiverData.resultData = resultData;
        receiverData.intent = intent;
        receiverData.component = component;
        receiverData.processName = processName;
        sendMessage(RECEIVER, receiverData);
    }

跟到消息队列中:

case RECEIVER: {
     handleReceiver((ReceiverData) msg.obj);
}
private void handleReceiver(ReceiverData data) {
        BroadcastReceiver.PendingResult result = data.resultData.build();
        try {
            // 依然是检测 Application 是否初始化,没有则初始化
            if (!isBound()) {
                bindApplication(data.component.getPackageName(), data.processName);
            }
            // 获取 Receiver 的 Context,这个context是一个ReceiverRestrictedContext实例,它有两个主要函数被禁掉:registerReceiver()和 bindService()。这两个函数在BroadcastReceiver.onReceive()不允许调用。每次Receiver处理一个广播,传递进来的context都是一个新的实例。
            Context context = mInitialApplication.getBaseContext();
            Context receiverContext = ContextImpl.getReceiverRestrictedContext.call(context);
            String className = data.component.getClassName();
            // 实例化目标 Receiver
            BroadcastReceiver receiver = (BroadcastReceiver) context.getClassLoader().loadClass(className).newInstance();
            mirror.android.content.BroadcastReceiver.setPendingResult.call(receiver, result);
            data.intent.setExtrasClassLoader(context.getClassLoader());
            // 手动调用 onCreate
            receiver.onReceive(receiverContext, data.intent);
            // 通知 Pending 结束
            if (mirror.android.content.BroadcastReceiver.getPendingResult.call(receiver) != null) {
                result.finish();
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(
                    "Unable to start receiver " + data.component
                            + ": " + e.toString(), e);
        }
        // 这里需要远程通知 VAService 广播已送到
        VActivityManager.get().broadcastFinish(data.resultData);
    }

这里就是最关键的地方了,简单点概括就是 new 了真正的 Receiver 然后调用 onCreate 而已。Receiver 生命周期真的非常简单。

需要注意的是,broadCast 发送有个超时机制:

void broadcastFinish(PendingResultData res) {
        synchronized (mBroadcastRecords) {
            BroadcastRecord record = mBroadcastRecords.remove(res.mToken);
            if (record == null) {
                VLog.e(TAG, "Unable to find the BroadcastRecord by token: " + res.mToken);
            }
        }
        mTimeoutHandler.removeMessages(0, res.mToken);
        res.finish();
    }
private final class TimeoutHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            IBinder token = (IBinder) msg.obj;
            BroadcastRecord r = mBroadcastRecords.remove(token);
            if (r != null) {
                VLog.w(TAG, "Broadcast timeout, cancel to dispatch it.");
                r.pendingResult.finish();
            }
        }
    }

这里如果广播超时则会通知 PendingResult 结束,告诉发送方广播结束了。

发送广播的处理

其实上一部分已经讲了很多发送广播的处理了。
这里 Hook 了 broacastIntent 方法:

static class BroadcastIntent extends MethodProxy {

        @Override
        public String getMethodName() {
            return "broadcastIntent";
        }

        @Override
        public Object call(Object who, Method method, Object... args) throws Throwable {
            Intent intent = (Intent) args[1];
            String type = (String) args[2];
            intent.setDataAndType(intent.getData(), type);
            if (VirtualCore.get().getComponentDelegate() != null) {
                VirtualCore.get().getComponentDelegate().onSendBroadcast(intent);
            }
            Intent newIntent = handleIntent(intent);
            if (newIntent != null) {
                args[1] = newIntent;
            } else {
                return 0;
            }

            if (args[7] instanceof String || args[7] instanceof String[]) {
                // clear the permission
                args[7] = null;
            }
            return method.invoke(who, args);
        }


        private Intent handleIntent(final Intent intent) {
            final String action = intent.getAction();
            if ("android.intent.action.CREATE_SHORTCUT".equals(action)
                    || "com.android.launcher.action.INSTALL_SHORTCUT".equals(action)) {

                return VASettings.ENABLE_INNER_SHORTCUT ? handleInstallShortcutIntent(intent) : null;

            } else if ("com.android.launcher.action.UNINSTALL_SHORTCUT".equals(action)) {

                handleUninstallShortcutIntent(intent);

            } else {
                return ComponentUtils.redirectBroadcastIntent(intent, VUserHandle.myUserId());
            }
            return intent;
        }

        private Intent handleInstallShortcutIntent(Intent intent) {
            Intent shortcut = intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
            if (shortcut != null) {
                ComponentName component = shortcut.resolveActivity(VirtualCore.getPM());
                if (component != null) {
                    String pkg = component.getPackageName();
                    Intent newShortcutIntent = new Intent();
                    newShortcutIntent.setClassName(getHostPkg(), Constants.SHORTCUT_PROXY_ACTIVITY_NAME);
                    newShortcutIntent.addCategory(Intent.CATEGORY_DEFAULT);
                    newShortcutIntent.putExtra("_VA_|_intent_", shortcut);
                    newShortcutIntent.putExtra("_VA_|_uri_", shortcut.toUri(0));
                    newShortcutIntent.putExtra("_VA_|_user_id_", VUserHandle.myUserId());
                    intent.removeExtra(Intent.EXTRA_SHORTCUT_INTENT);
                    intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, newShortcutIntent);

                    Intent.ShortcutIconResource icon = intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
                    if (icon != null && !TextUtils.equals(icon.packageName, getHostPkg())) {
                        try {
                            Resources resources = VirtualCore.get().getResources(pkg);
                            int resId = resources.getIdentifier(icon.resourceName, "drawable", pkg);
                            if (resId > 0) {
                                //noinspection deprecation
                                Drawable iconDrawable = resources.getDrawable(resId);
                                Bitmap newIcon = BitmapUtils.drawableToBitmap(iconDrawable);
                                if (newIcon != null) {
                                    intent.removeExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
                                    intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, newIcon);
                                }
                            }
                        } catch (Throwable e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            return intent;
        }
  1. 这里拦截了创建快捷图标的 Intent,这是一个发给 Launcher 的隐式广播,VA 把这个请求拦截下来因为如果不拦截这个快捷方式就会指向外部的 App,并且如果外部 App 没有安装,此广播也不会发生作用。VA 把这个广播换成了自己的逻辑。
  2. 注意 ComponentUtils.redirectBroadcastIntent(), 类似 Activity 用代理 Intent 包裹真正的 Intent:
public static Intent redirectBroadcastIntent(Intent intent, int userId) {
        Intent newIntent = intent.cloneFilter();
        newIntent.setComponent(null);
        newIntent.setPackage(null);
        ComponentName component = intent.getComponent();
        String pkg = intent.getPackage();
        if (component != null) {
            newIntent.putExtra("_VA_|_user_id_", userId);
            // 这里显式意图被重定位成 _VA_PKGNAME_CLASSNAME 的格式,与前面注册的时候对应
            newIntent.setAction(String.format("_VA_%s_%s", component.getPackageName(), component.getClassName()));
            newIntent.putExtra("_VA_|_component_", component);
            newIntent.putExtra("_VA_|_intent_", new Intent(intent));
        } else if (pkg != null) {
            newIntent.putExtra("_VA_|_user_id_", userId);
            newIntent.putExtra("_VA_|_creator_", pkg);
            newIntent.putExtra("_VA_|_intent_", new Intent(intent));
            String protectedAction = SpecialComponentList.protectAction(intent.getAction());
            if (protectedAction != null) {
                newIntent.setAction(protectedAction);
            }
        } else {
            newIntent.putExtra("_VA_|_user_id_", userId);
            newIntent.putExtra("_VA_|_intent_", new Intent(intent));
            String protectedAction = SpecialComponentList.protectAction(intent.getAction());
            if (protectedAction != null) {
                newIntent.setAction(protectedAction);
            }
        }
        return newIntent;
    }

Ok, BroadcastReceiver 完毕,下一张分析最后一个组件 ContentProvider。

转载:https://blog.youkuaiyun.com/ganyao939543405/article/details/76229480

进击的Android注入术《五》 继续 在Android,几乎所有的IPC通讯都是 阅读详情

相关推荐

浊音/清音/爆破音的时域及频域特性

第一周作业 浊音/清音/爆破音的时域及频域特性 思路:使用Audacity进行录制,时域部分将音频放大观察波形规律,频域部分生成并进行频谱分析。 (1)浊音U 时域分析: 时域表现为短周期性,波形比较规律。 频域分析: 频域上表现为频率集中在低频区域,共振峰较为明显,短时能量较高。 (2)清音S 时域分析: 时域波形没有周期性,整体振幅小于浊音。 频域分析: 频域波形平缓,无共振峰,能量比较集中。 (3)爆破音t 时域分析: 脉冲波,更为尖锐,整体上振幅降低。 频域分析: 没有像浊音一样的共振

Leylaaaa的博客 1228

VirtualApp原理解析(2)--后台服务初始化流程

VirtualApp原理解析–后台服务初始化流程VirtualApp后台服务进程运行BinderProvider,其继承自ContentProvider。后台服务主要类:VAppMS,VAMS,VPMS等。主要功能:数据结构组建状态保存、模拟系统API的实现、预处理等。

leif 的博客 4208

金智维KRPA实战:Excel自动化数据处理全流程解析

本文详细解析了金智维KRPA在Excel自动化数据处理中的全流程应用,涵盖COM接口和文件读写两种核心技术方案,以及销售报表生成、数据清洗和可视化仪表盘等实战案例。通过企业级应用的异常处理、性能优化和安全合规实践,显著提升数据处理效率和准确性,助力企业降本增效。

weixin_30329623的博客 598

Android 双开沙箱 VirtualApp 源码分析(三)App 启动

上一章:Android 双开沙箱 VirtualApp 源码分析(二) 在这之前,我们还是要先了解一下 VA Client Framework 和 VAService 之间的通讯方式VAService 与通讯VAService首先,VAService 是指 VA 仿造 Android 原生 framework 层 Service 实现的一套副本,举例有 VActivityManagerServic

ganyao939543405的博客 1万+

Python 上位机开发

首先,什么是上位机?上位机通常是指可以与下位机(如单片机、传感器等硬件设备)进行通信和交互,实现数据采集、控制指令发送以及数据处理和展示的计算机程序。 Python 作为一种高级编程语言,在开发上位机方面具有诸多优势。它拥有丰富的第三方库,使得开发过程变得相对简单和高效。

weixin_46807151的博客 4302

android广告SDK原理详解(附源码)

广大的开发者吃糠咽菜开发了一两款APP,获取了一些流量后自然就会想到流量变现,一般情况下大家会选择到百度联盟或者Google Admob这些广告服务提供商注册开发者账号,在自己的APP里面加上几行广告位代码,集成他们的广告SDK包。然后谋事在人,成事在天,只有每天看报表查查收益。   也有一些开发者为了能够提高自己的变现水平,同时接入了好几家的sdk,哪家出的收益高一点我就出哪家的广告,但是这

rmcgogo的博客 1万+

VirtualApp沙盒基本原理

转自:http://rk700.github.io/2017/03/15/virtualapp-basic/ VirtualApp是一个开源的Android App虚拟化引擎,允许在其中创建虚拟空间,并在这个虚拟空间中运行其他应用。通过阅读源码及动态调试,基本了解了其运行原理,在此记录。 本质 Android应用隔离是基于Linux系统的多用户机制实现的,即每个应用在安装

云守护的专栏 2万+

VSCode配置C/C++开发环境:从零安装MinGW-w64并运行第一个C程序

在Windows系统上搭建C语言开发环境时,很多初学者混淆编辑器、编译器与调试器的分工,导致配置过程屡屡受挫。理解VSCode作为轻量级编辑器的定位,以及MinGW-w64提供的gcc编译器在源码编译中的核心作用,是顺利完成环境配置的关键。环境变量的正确设置直接影响gcc命令能否全局调用,而C/C++插件则赋予编辑器语法高亮与智能提示能力。通过安装VSCode、配置MinGW-w64、安装插件、汉化界面并运行一个最简单的hello.c程序,就能系统掌握这套开源工具链的完整工作流。无论是用于课程学习、算法练习

weixin_42531923的博客 531

VirtualApp 中的进程

一. 什么是进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。 – 来自百度百科 二. VA进程VA在运行时,存在四种类型的进程:

ximsfei的专栏 2811

Unity卡牌游戏UI框架:数据驱动与模块化设计实践

在Unity游戏开发中,UI系统是连接玩家与游戏逻辑的关键桥梁,其核心在于实现数据与表现的高效同步。数据驱动设计通过分离数据模型、逻辑状态与视图表现,利用观察者模式自动响应状态变化,确保了代码的清晰与可维护性。这种架构显著提升了开发效率,尤其在需要复杂状态管理和实时反馈的交互场景中,如卡牌游戏的拖拽、布局与动画系统。基于模块化思想,开发者可以将卡牌视图、布局管理、区域控制等组件像积木一样组合,快速构建出动态的手牌弧形布局、战场区域管理等特色功能。通过集成对象池、图集优化等性能方案,还能有效管理绘制调用与网格

weixin_34262482的博客 453

SAP物料分类账(ML)避坑指南:激活顺序错了怎么办?从CKMSTART到差异分摊的完整流程

本文详细解析SAP物料分类账(ML)的正确激活顺序与配置流程,帮助用户避免常见陷阱如'LKW无科目'、'在制品重估失败'等问题。从CKMSTART到差异分摊的完整流程,涵盖关键配置节点、典型报错分析与应急处理方案,确保产品成本控制(CO-PC)模块的顺利运行。

weixin_42524708的博客 258

VirtualApp 进程分配与管理

一. 前景 在Android中每个App在启动前必须先创建一个进程,该进程是由Zygote fork出来的,进程具有独立的资源空间,用于承载App上运行的各种Activity/Service等组件。进程对于上层应用来说是完全透明的,这也是google有意为之,让App程序都是运行在Android Runtime。大多数情况一个App就运行在一个进程中,除非在AndroidManifest.xml

ximsfei的专栏 3983

【CANdelaStudio编辑CDD】-1.2-给$10服务SessionControl创建一个新Session会话并修改Session state machine会话状态机(UDS诊断)

前一篇:【UDS诊断描述文件CDD】-1.1-ISO_14229中$10服务理论部分https://blog.youkuaiyun.com/qfmzhu/article/details/120375515案例背景: 在CDD中如何给$10服务创建一个新的Session会话; 在CDD中如何给$10服务修改Session state machine会话状态机。 目录 1 查看$10服务中已有的Session会话​ 2 查看$10服务中已有的Session state machine会话状态机 3 在$10...

「汽车电子助手」的博客 6074

VirtualApp原理解析(1)--初始化及注入流程

VirtualApp原理解析 初始化及注入流程 VirtualApp是一款可以达到应用双开效果的开源项目。VirtualApp整体流程,包含启动双开应用Activity的过程。

leif 的博客 1万+

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

virtualapp-IPCBus跨进程通讯(转载)

IPCBus跨进程通讯

hgy413的专栏 1083

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Android 双开沙箱 VirtualApp 源码分析(六)ContentProvider

上一章:Android 双开沙箱 VirtualApp 源码分析(五)BroadcastReceiverProvider 注册回顾前面,Activity 启动的时候会检查 Application 是否初始化,会调用 bindApplication,里面执行了安装 Provider 的方法:private void installContentProviders(Context app, List<P

ganyao939543405的博客 7051

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

BroadCast Receiver(广播)详解

BroadCastReceiver是四大组件之一,是一个全局的监听器BroadCastReceiver有两大功能,广播发送者,广播接收者BroadCastReceiver分为静态广播和动态动态广播。

suiyue010211的博客 8405

Android沙箱

Android沙箱是一种隔离机制,用于在Android系统中保护应用程序的运行环境和数据安全。每个Android应用程序都运行在自己的沙箱中,类似于一个隔离的空间,防止它与其他应用程序或系统资源直接交互。这种设计主要依赖于Linux内核的用户隔离和权限管理功能,以确保应用之间的数据和功能互不干扰。

Bileton的博客 2378

【Android 插件化】VirtualApp 编译运行 ( VirtualApp 简介 | 配置 VirtualApp 编译环境 | 编译运行 VirtualApp 官方示例 )

一、VirtualApp 简介、 二、配置 VirtualApp 编译环境、 1、Android Studio 3.0、 2、NDK 版本 ( Android NDK r10e )、 3、配置远程仓库、 三、编译运行 VirtualApp 官方示例、 四、编译好的 VirtualApp 官方源码、 总结 ( 编译配置如下 )、

让 学习 成为一种 习惯 ( 韩曙亮 の 技术博客 ) 7338

【Android Framework系列】第17章 Android Q沙箱模式(Scoped Storage)

App TargetSDK > 28 即 Android10(Q)及以上项目,Google限制了存储沙箱模式,在Android10(Q)以上的设备建议使用私有目录data/data,无法再直接访问外部SD卡存储目录,如需要使用外部SD卡存储目录则需要通过SAF、MediaStore接口访问,并且只能访问特定的外部SD卡存储目录,如Downloads、Documents、Pictures 、DCIM、Movies、Music、Ringtones

Yvan 3077

Android 双开沙箱 VirtualApp 源码分析(一)

最近发现了一个非常好的开源项目,基本实现了一个 Android 上的沙箱环境,不过应用场景最多的还是应用双开。 VA github: https://github.com/asLody/VirtualApp VA 的源码注释: https://github.com/ganyao114/VA_Doc 第一章主要是分析一下项目的整体结构。包结构android.content主要是 PackageP

ganyao939543405的博客 4万+

Android的SandBox(沙箱)

在研究Android系统时,有时会遇到Sandbox(沙箱)这个概念。沙箱概念本身并不是太新奇,但是一直不是很清楚Android是如何实现它所称的“沙箱”的。网上不少人声称应用使用了虚拟机就是应用了沙箱,一直对这种说法表示怀疑。 最近发现Android的网站上更新了一些文档,其中包括对Sandbox的解释,这才明白Android中Sandbox的含义。 Android的”沙箱“

u013234805的专栏 9816

Android 双开沙箱 VirtualApp 源码分析(四)启动插件 Service

上一章:Android 双开沙箱 VirtualApp 源码分析(三)App 启动原生 Service 创建过程首先有必要了解一下原生 framework 对 Service 的创建,因为在 VA 中启动 Service 和 Activity 有很大的区别。首先入口 ContextWrapper.startService():@Override public ComponentName st

ganyao939543405的博客 7800

(4.1.55)Android 沙箱机制

(4.1.55)Android 沙箱机制 沙箱(sandbox)是为执行中的程序提供隔离环境的一种安全机制。它通过严格控制执行的程序所访问的资源,以确保系统的安全。 文章目录一、Android系统架构二、Android安全机制三、Android沿用Linux权限模型四、Android沙箱模型4.1 应用程序在独立的进程4.2 应用程序在同一个进程(共享UID)参考文献 一、Android系统架...

JAVA成长之路 1万+

Android系统广播(3)–客户端广播处理流程

Android系统广播(3)–客户端广播处理流程

leif 的博客 929
上一篇: Android 双开沙箱 VirtualApp 源码分析(四)启动插件 Service
下一篇: Android 双开沙箱 VirtualApp 源码分析(六)ContentProvider
CoderAldrich
博客等级 码龄12年 431粉丝 24原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值