aosp15 shelltransition之桌面点击启动app部分-systemserver部分

背景:

本节课开始带大家来开始分析ShellTransition的最开始的WMCore端启动Transition部分的相关的内容及源码剖析。
在这里插入图片描述

源码分析部分:

frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java

private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
        int startFlags, ActivityOptions options, Task inTask,
        TaskFragment inTaskFragment,
        BalVerdict balVerdict,
        NeededUriGrants intentGrants, int realCallingUid) {
   

    // Create a transition now to record the original intent of actions taken within
    // startActivityInner. Otherwise, logic in startActivityInner could start a different
    // transition based on a sub-action.
    // Only do the create here (and defer requestStart) since startActivityInner might abort.
    final TransitionController transitionController = r.mTransitionController;
    //创建对应的Transition
    Transition newTransition = transitionController.isShellTransitionsEnabled()
            ? transitionController.createAndStartCollecting(TRANSIT_OPEN) : null;
    RemoteTransition remoteTransition = r.takeRemoteTransition();

        mService.deferWindowLayout();
        
    //把ActivityRecord进行collect收集处理
        transitionController.collect(r);
        try {
            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
            result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
                    startFlags, options, inTask, inTaskFragment, balVerdict,
                    intentGrants, realCallingUid);
        } catch (Exception ex) {
            Slog.e(TAG, "Exception on startActivityInner", ex);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
            
    //handleStartResult中会对transition进行requestStartTransition处理
            startedActivityRootTask = handleStartResult(r, options, result, newTransition,
                    remoteTransition);
}

上面startActivityUnchecked中主要有3个部分和transition相关步骤

1、创建并启动收集对应的Transition

Transition newTransition = transitionController.isShellTransitionsEnabled()
            ? transitionController.createAndStartCollecting(TRANSIT_OPEN) : null;

这里transitionController.createAndStartCollecting(TRANSIT_OPEN)就是创建出对应的Transition

frameworks/base/services/core/java/com/android/server/wm/TransitionController.java

Transition createAndStartCollecting(int type) {
 //构建Transition
    Transition transit = new Transition(type, 0 /* flags */, this, mSyncEngine);
 //调用moveToCollecting
    moveToCollecting(transit);
    return transit;
}
  /** Starts Collecting */
   void moveToCollecting(@NonNull Transition transition) {
      
       mCollectingTransition = transition;
	//实际调用了Transition的startCollecting
       mCollectingTransition.startCollecting(timeoutMs);
    
   }

核心重点就是startCollecting方法
frameworks/base/services/core/java/com/android/server/wm/Transition.java

    /** Starts collecting phase. Once this starts, all relevant surface operations are sync. */
    void startCollecting(long timeoutMs) {
      
        //设置状态为COLLECTING
        mState = STATE_COLLECTING;
        //调用mSyncEngine方法获取mSyncId
        mSyncId = mSyncEngine.startSyncSet(this, timeoutMs,
                TAG + "-" + transitTypeToString(mType),
                mParallelCollectType != PARALLEL_TYPE_NONE);
        mSyncEngine.setSyncMethod(mSyncId, TransitionController.SYNC_METHOD);
    }

这里主要看看mSyncEngine.startSyncSet方法
frameworks/base/services/core/java/com/android/server/wm/BLASTSyncEngine.java

   int startSyncSet(TransactionReadyListener listener, long timeoutMs, String name,
            boolean parallel) {
        final SyncGroup s = prepareSyncSet(listener, name);
        startSyncSet(s, timeoutMs, parallel);
        return s.mSyncId;
    }
      /**
     * Prepares a {@link SyncGroup} that is not active yet. Caller must call {@link #startSyncSet}
     * before calling {@link #addToSyncSet(int, WindowContainer)} on any {@link WindowContainer}.
     */
    SyncGroup prepareSyncSet(TransactionReadyListener listener, String name) {
        return new SyncGroup(listener, mNextSyncId++, name);
    }
    
   void startSyncSet(SyncGroup s, long timeoutMs, boolean parallel) {
    	//新建的SyncGroup放入到mActiveSyncs
        mActiveSyncs.add(s);
        // For now, parallel implies this.
        s.mIgnoreIndirectMembers = parallel;
        scheduleTimeout(s, timeoutMs);
    }

总结一下transitionController.createAndStartCollecting干的事情
1、创建出systemserver层面的一个Transition
2、调用Transition的startCollecting设置Transition的mState为STATE_COLLECTING
3、调用BLASTSyncEngine创建一个SyncGroup,每个SyncGroup有一个固定的SyncId
4、把创建的SyncGroup放入到BLASTSyncEngine的mActiveSyncs集合中

2、正式收集collect对应WindowContainer

上面第一步只是处于收集中的一种状态,并不是真正收集,在第二步就是正式开始收集各个WindowContainer
transitionController.collect®方法进行剖析
frameworks/base/services/core/java/com/android/server/wm/TransitionController.java

/** @see Transition#collect */
void collect(@NonNull WindowContainer wc) {
    if (mCollectingTransition == null) return;
    //mCollectingTransition就是第一步new的Transition
    mCollectingTransition.collect(wc);
}

这里实际调用到了Transition的collect

/**
     * Adds wc to set of WindowContainers participating in this transition.
     */
    void collect(@NonNull WindowContainer wc) {
      //这里判断一定要属于Collecting状态
        if (!isCollecting()) {
            // Too late, transition already started playing, so don't collect.
            return;
        }
        if (!isInTransientHide(wc)) {
        //调用addToSyncSet把wc放入到SyncEngine中
            mSyncEngine.addToSyncSet(mSyncId, wc);
        }
      
        ChangeInfo info = mChanges.get(wc);
        if (info == null) {
        //针对wc构造对应的ChangeInfo
            info = new ChangeInfo(wc);
            updateTransientFlags(info);
            //加入到mChanges集合中
            mChanges.put(wc, info);
        }
        //把wc加入到Transition的mParticipants集合中
        mParticipants.add(wc);
        recordDisplay(wc.getDisplayContent());
        if (info.mShowWallpaper) {
            // Collect the wallpaper token (for isWallpaper(wc)) so it is part of the sync set.
            wc.mDisplayContent.mWallpaperController.collectTopWallpapers(this);
        }
    }
		boolean isCollecting() {
        return mState == STATE_COLLECTING || mState == STATE_STARTED;
    }

collect里面主要有3个部分,addToSyncSet添加wc,通过wc构造ChangeInfo,mParticipants集合添加wc。
这里重点来看看addToSyncSet方法
frameworks/base/services/core/java/com/android/server/wm/BLASTSyncEngine.java

   void addToSyncSet(int id, WindowContainer wc) {
        getSyncGroup(id).addToSync(wc);
    }

private void addToSync(WindowContainer wc) {
            if (mRootMembers.contains(wc)) {
                return;
            }
            final SyncGroup dependency = wc.getSyncGroup();
            if (dependency != null && dependency != this && !dependency.isIgnoring(wc)) {
              
            } else {
                mRootMembers.add(wc);//添加到mRootMembers的集合中
                wc.setSyncGroup(this);//让WindowContainer与SyncGroup产生链接
            }
            wc.prepareSync();//调用WindowContainer的prepareSync方法
        }

addToSyncSet主要就是
1、添加到mRootMembers的集合中
2、让WindowContainer与SyncGroup产生链接
3、调用WindowContainer的prepareSync方法
下面继续看看prepareSync方法
frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

    boolean prepareSync() {
        if (mSyncState != SYNC_STATE_NONE) {
            // Already part of sync
            return false;
        }
        //这里会遍历WindowContainer的所有child,然后让child也进行prepareSync
        for (int i = getChildCount() - 1; i >= 0; --i) {
            final WindowContainer child = getChildAt(i);
            child.prepareSync();
        }
        //核心就是把WindowContainer的mSyncState设置成了SYNC_STATE_READY状态
        mSyncState = SYNC_STATE_READY;
        return true;
    }

可以看出WindowContainer的prepareSync主要就是对mSyncState变量变成SYNC_STATE_READY。

3、requestStartTransition剖析

 /** Asks the transition player (shell) to start a created but not yet started transition. */
    @NonNull
    Transition requestStartTransition(@NonNull Transition transition, @Nullable Task startTask,
            @Nullable RemoteTransition remoteTransition,
            @Nullable TransitionRequestInfo.DisplayChange displayChange) {
       //省略部分
			//构造出对应TransitionRequestInfo
            final TransitionRequestInfo request = new TransitionRequestInfo(transition.mType,
                    startTaskInfo, pipTaskInfo, remoteTransition, displayChange,
                    transition.getFlags(), transition.getSyncId());
					//跨进程通讯通知到systemui端
            mTransitionPlayers.getLast().mPlayer.requestStartTransition(
                    transition.getToken(), request);
                    //这里把remoteTransition设置给transition
            if (remoteTransition != null) {
                transition.setRemoteAnimationApp(remoteTransition.getAppThread());
            }

        return transition;
    }

这个requestStartTransition执行后,就会跨进程通讯到wmshell端,即就是systemui进程,接下来就是要systemui进程来负责相关的Transition的业务。

更多framework实战干货,请关注下面“千里马学框架”

<think>我们正在处理的是一个Android AOSP系统定制需求:在系统级别屏蔽特定应用,使其不出现在桌面(Launcher)和应用设置中。根据引用[2]的内容,我们知道在Launcher3(AOSP默认桌面)中,可以通过修改加载和更新应用列表的代码来过滤特定应用。 具体步骤: 1. **修改加载应用的代码**:在`LoadTask.java`中,过滤掉不需要显示的应用。 2. **修改应用更新时的代码**:在`PackageUpdatedTask.java`中,确保在安装、更新或卸载应用时同样应用过滤规则。 此外,根据用户需求,还需要在设置中屏蔽该应用。这可以通过修改设置应用(Settings)的代码来实现,通常是在显示应用列表的地方进行过滤。 系统级隐藏(用户不可见)的深度解决方案: ### 一、桌面(Launcher3)屏蔽应用 根据引用[2],我们需要修改两个文件: 1. **`LoadTask.java`** 该文件负责加载所有已安装应用。在加载过程中,我们可以添加一个条件判断,跳过需要隐藏的应用。 代码修改示例(在`LoadTask.java`的`loadAllApps`方法中): ```java for (PackageInfo info : packages) { // 需要隐藏的应用包名列表,例如:com.example.hiddenapp if (shouldHideApp(info.packageName)) { continue; // 跳过此应用 } // 正常处理其他应用 } ``` 其中,`shouldHideApp`是一个自定义方法,用于判断应用是否在隐藏名单中。 2. **`PackageUpdatedTask.java`** 该文件负责处理应用安装、更新和卸载事件。在更新应用列表时,同样需要过滤隐藏应用。 代码修改示例(在`PackageUpdatedTask.java`的`execute`方法中): ```java for (PackageInfo info : mUpdatedPackages) { if (shouldHideApp(info.packageName)) { continue; // 跳过隐藏应用 } // 处理其他应用更新 } ``` ### 二、设置(Settings)中屏蔽应用 在设置应用中,我们需要修改显示应用列表的地方。通常,设置应用会通过`PackageManager`获取所有已安装应用。我们可以通过以下方式修改: 1. **修改查询条件**:在查询应用列表时,排除隐藏应用。 2. **修改适配器**:在填充应用列表的适配器中过滤掉隐藏应用。 具体修改位置可能在`ManageApplications.java`或类似文件中(不同Android版本可能不同)。 示例代码(在查询应用列表时): ```java List<ApplicationInfo> apps = packageManager.getInstalledApplications(flags); // 过滤隐藏应用 Iterator<ApplicationInfo> iterator = apps.iterator(); while (iterator.hasNext()) { ApplicationInfo appInfo = iterator.next(); if (shouldHideApp(appInfo.packageName)) { iterator.remove(); } } ``` ### 三、系统级全局隐藏(可选) 如果希望该应用在整个系统中都不可见(包括第三方应用获取应用列表时),可以通过修改`PackageManagerService`实现。但这样可能会影响系统稳定性,且需要重新编译整个系统。 在`PackageManagerService`中,修改`queryIntentActivities`、`queryIntentServices`等方法,在返回结果前移除隐藏应用。 > ⚠️ 注意:此修改影响范围大,需谨慎。 ### 四、配置隐藏应用名单 我们可以通过系统属性、配置文件或数据库来存储需要隐藏的应用包名列表。例如,在`/system/etc/hidden_apps.xml`中定义隐藏应用名单,然后在代码中读取。 示例`hidden_apps.xml`: ```xml <?xml version="1.0" encoding="utf-8"?> <hidden-apps> <item package="com.example.hiddenapp1"/> <item package="com.example.hiddenapp2"/> </hidden-apps> ``` 读取该配置的代码: ```java private boolean shouldHideApp(String packageName) { // 从配置文件中读取隐藏应用名单,判断当前包名是否在名单中 return hiddenAppsList.contains(packageName); } ``` ### 五、编译与部署 修改完成后,需要重新编译`Launcher3`、`Settings`和`framework`(如果修改了`PackageManagerService`)模块,并将生成的系统镜像刷入设备。 编译命令示例: ```bash mmm packages/apps/Launcher3/ mmm packages/apps/Settings/ ``` 或者编译整个系统: ```bash make -j8 ``` §§相关问题§§ 1. 如何在Android系统中动态更新隐藏应用名单而无需重新编译? 2. 修改PackageManagerService隐藏应用可能带来哪些兼容性问题? 3. 如何防止隐藏的应用被其他应用通过包名直接启动? 4. 在定制Android系统时,如何为不同用户配置不同的应用隐藏策略? 5. 隐藏系统预装应用与隐藏第三方应用在实现上有何区别? </think>在Android AOSP系统中实现系统级应用屏蔽,需修改Launcher3和系统服务代码,以下是核心实现方案: ### 一、Launcher3层屏蔽(桌面不显示) 在`workspace app列表页`加载逻辑中过滤特定应用,需修改两处关键文件: 1. **加载时过滤 (`LoadTask.java`)** 修改应用加载逻辑,添加包名过滤: ```java // packages/apps/Launcher3/src/com/android/launcher3/model/LoadTask.java protected Void run() { List<AppInfo> allApps = new ArrayList<>(); for (PackageInfo pkg : installedPackages) { // 跳过指定包名的应用 if (pkg.packageName.equals("com.target.package")) continue; allApps.add(new AppInfo(...)); } mBgAllAppsList.add(allApps); } ``` 2. **动态更新过滤 (`PackageUpdatedTask.java`)** 处理应用安装/卸载事件时的过滤: ```java // packages/apps/Launcher3/src/com/android/launcher3/model/PackageUpdatedTask.java public void execute(...) { for (String pkg : modifiedPackages) { if (pkg.equals("com.target.package")) { // 跳过更新目标应用 continue; } updatePackage(pkg); // 正常更新其他应用 } } ``` ### 二、设置菜单层屏蔽 修改设置应用逻辑,隐藏目标应用: 1. **应用列表过滤 (`ManageApplications.java`)** ```java // packages/apps/Settings/src/com/android/settings/applications/ManageApplications.java private void rebuild() { List<ApplicationInfo> apps = mPm.getInstalledApplications(flags); Iterator<ApplicationInfo> it = apps.iterator(); while (it.hasNext()) { if (it.next().packageName.equals("com.target.package")) { it.remove(); // 移除非显示应用 } } mAdapter.setApps(apps); } ``` 2. **禁用设置入口 (`AndroidManifest.xml`)** 在目标应用的AndroidManifest中添加: ```xml <activity android:name=".TargetActivity"> <intent-filter> <action android:name="android.intent.action.APPLICATION_PREFERENCES" /> <!-- 屏蔽设置入口 --> </intent-filter> </activity> ``` ### 三、系统服务层屏蔽(可选) 彻底隐藏应用需修改`PackageManagerService`: ```java // frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java public List<PackageInfo> getInstalledPackages(int flags, int userId) { List<PackageInfo> filteredList = new ArrayList<>(); for (PackageInfo pkg : originalList) { if (!pkg.packageName.equals("com.target.package")) { filteredList.add(pkg); // 系统级过滤 } } return filteredList; } ``` ### 四、实现效果验证 1. **编译刷机** 使用以下命令编译模块: ```bash mmm packages/apps/Launcher3/ mmm packages/apps/Settings/ adb reboot bootloader fastboot flash system system.img ``` 2. **验证逻辑** - 桌面不再显示目标应用图标 - 设置 → 应用列表不显示目标应用 - `adb shell pm list packages` 仍可见包名(证明未卸载) > ️ **注意事项** > 1. 需修改`hidden_api_blacklist.txt`绕过隐藏API限制 > 2. 系统签名`platform.pk8`和`platform.x509.pem` > 3. 需处理`PackageMonitor`监听器的兼容性[^2][^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值