Android 11 替换Launcher后导致近期任务无效

平台

     RK3566 + Android 11

概述

    替换默认主界面, 更换为指定第三方Launcher后, 点击导航栏的RECENT键无效. 究其原因在于, 在旧版本SDK上, 删除Launcher3并不会影响RECENT的功能 , 而在新的SDK上, RECENT功能集成于Launcher3目录下, 删除 Launcher3后, 导致SystemUI调用对应的RECENT界面启动的服务失败.

过程疏理

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    
    
        super.onViewCreated(view, savedInstanceState);
        mNavigationBarView = (NavigationBarView) view;
        final Display display = view.getDisplay();
        // It may not have display when running unit test.
        if (display != null) {
    
    
            mDisplayId = display.getDisplayId();
            mIsOnDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
        }

        mNavigationBarView.setComponents(mStatusBarLazy.get().getPanelController());
        mNavigationBarView.setDisabledFlags(mDisabledFlags1);
        mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
        mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
        if (savedInstanceState != null) {
    
    
            mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState);
        }
        mNavigationBarView.setNavigationIconHints(mNavigationIconHints);
        mNavigationBarView.setWindowVisible(isNavBarWindowVisible());

        prepareNavigationBarView();
    }

    private void prepareNavigationBarView() {
    
    
        mNavigationBarView.reorient();

        ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
        recentsButton.setOnClickListener(this::onRecentsClick);
        recentsButton.setOnTouchListener(this::onRecentsTouch);
        recentsButton.setLongClickable(true);
        recentsButton.setOnLongClickListener(this::onLongPressBackRecents);
    }

    private void onRecentsClick(View v) {
    
    
        if (LatencyTracker.isEnabled(getContext())) {
    
    
            LatencyTracker.getInstance(getContext()).onActionStart(
                    LatencyTracker.ACTION_TOGGLE_RECENTS);
        }
        mStatusBarLazy.get().awakenDreams();
        mCommandQueue.toggleRecentApps();
    }

frameworks/base/packages/SystemUI/src/com/android/systemui/recents/Recents.java

   @Override
    public void toggleRecentApps() {
    
    
        // Ensure the device has been provisioned before allowing the user to interact with
        // recents
        if (!isUserSetup()) {
    
    
            return;
        }

        mImpl.toggleRecentApps();
    }

frameworks/base/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java

   @Override
    public void toggleRecentApps() {
    
    
        Log.d(TAG, "toggleRecentApps");
        // If connected to launcher service, let it handle the toggle logic
        IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
        if (overviewProxy != null) {
    
    
            final Runnable toggleRecents = () -> {
    
    
                try {
    
    
                    if (mOverviewProxyService.getProxy() != null) {
    
    
                        mOverviewProxyService.getProxy().onOverviewToggle();
                        mOverviewProxyService.notifyToggleRecentApps();
                    }
                } catch (RemoteException e) {
    
    
                    Log.e(TAG, "Cannot send toggle recents through proxy service.", e);
                }
            };
            // Preload only if device for current user is unlocked
            if (mStatusBarLazy != null && mStatusBarLazy.get().isKeyguardShowing()) {
    
    
                mStatusBarLazy.get().executeRunnableDismissingKeyguard(() -> {
    
    
                        // Flush trustmanager before checking device locked per user
                        mTrustManager.reportKeyguardShowingChanged();
                        mHandler.post(toggleRecents);
                    }, null,  true /* dismissShade */, false /* afterKeyguardGone */,
                    true /* deferred */);
            } else {
    
    
                toggleRecents.run();
            }
            return;
        } else {
    
    
            // Do nothing
        }
    }

重点看下overviewProxy

frameworks/base/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java

    private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";

    @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
    @Inject
    public OverviewProxyService(Context context, CommandQueue commandQueue,
            NavigationBarController navBarController, NavigationModeController navModeController,
            NotificationShadeWindowController statusBarWinController, SysUiState sysUiState,
            PipUI pipUI, Optional<Divider> dividerOptional,
            Optional<Lazy<StatusBar>> statusBarOptionalLazy,
            BroadcastDispatcher broadcastDispatcher) {
    
    
        super(broadcastDispatcher);
        mContext = context;
        mPipUI = pipUI;
        mStatusBarOptionalLazy = statusBarOptionalLazy;
        mHandler = new Handler();
        mNavBarController = navBarController;
        mStatusBarWinController = statusBarWinController;
        mConnectionBackoffAttempts = 0;
        mDividerOptional = dividerOptional;
//frameworks/base/core/res/res/values/config.xml
//    <string name="config_recentsComponentName" translatable="false">com.android.launcher3/com.android.quickstep.RecentsActivity</string>
        mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
                com.android.internal.R.string.config_recentsComponentName));
        mQuickStepIntent = new Intent(ACTION_QUICKSTEP)
    }

    //绑定服务
    private void internalConnectToCurrentUser() {
    
    
        //..............
        Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
                .setPackage(mRecentsComponentName.getPackageName());
        try {
    
    
            mBound = mContext.bindServiceAsUser(launcherServiceIntent,
                    mOverviewServiceConnection,
                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
                    UserHandle.of(getCurrentUserId()));
        } catch (SecurityException e) {
    
    
            Log.e(TAG_OPS, "Unable to bind because of security error", e);
        }
        //.....
    }
    //绑定成功
        private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
    
    
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
    
    
            //.........
            mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
        }

    //由上面OverviewProxyRecentsImpl 调用
    public IOverviewProxy getProxy() {
    
    
        return mOverviewProxy;
    }

近期任务UI的实现有别与旧版本的SDK, 有frameworks/base/packages/SystemUI改到了packages/apps/Launcher3/quickstep

packages/apps/Launcher3/quickstep/AndroidManifest.xml

            <service
            android:name="com.android.quickstep.TouchInteractionService"
            android:permission="android.permission.STATUS_BAR_SERVICE"
            android:directBootAware="true" >
            <intent-filter>
                <action android:name="android.intent.action.QUICKSTEP_SERVICE" />
            </intent-filter>
        </service>

为了使用默认第三方Launcher, 需要注释掉quickstep自带的Launcher

packages/apps/Launcher3/quickstep/AndroidManifest-launcher.xml

        <activity
            android:name="com.android.launcher3.uioverrides.QuickstepLauncher"
            android:launchMode="singleTask"
            android:clearTaskOnLaunch="true"
            android:stateNotNeeded="true"
            android:windowSoftInputMode="adjustPan"
            android:screenOrientation="unspecified"
            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
            android:resizeableActivity="true"
            android:resumeWhilePausing="true"
            android:taskAffinity=""
            android:enabled="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <!--category android:name="android.intent.category.HOME" /-->
                <category android:name="android.intent.category.DEFAULT" />
                <!--category android:name="android.intent.category.MONKEY"/-->
                <category android:name="android.intent.category.LAUNCHER_APP" />
            </intent-filter>
            <meta-data
                android:name="com.android.launcher3.grid.control"
                android:value="${packageName}.grid_control" />
        </activity>

开机奔溃

    2022-07-10 04:01:50.179 1472-1472/com.android.launcher3 E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.android.launcher3, PID: 1472
    java.lang.RuntimeException: Unable to create service com.android.quickstep.TouchInteractionService: java.lang.NullPointerException: Attempt to read from field 'android.content.pm.ActivityInfo android.content.pm.ResolveInfo.activityInfo' on a null object reference
        at android.app.ActivityThread.handleCreateService(ActivityThread.java:4198)
        at android.app.ActivityThread.access$1500(ActivityThread.java:237)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1932)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7664)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
     Caused by: java.lang.NullPointerException: Attempt to read from field 'android.content.pm.ActivityInfo android.content.pm.ResolveInfo.activityInfo' on a null object reference
        at com.android.quickstep.OverviewComponentObserver.<init>(OverviewComponentObserver.java:82)
        at com.android.quickstep.TouchInteractionService.onUserUnlocked(TouchInteractionService.java:345)
        at com.android.quickstep.-$$Lambda$6M6xH0rMyGjroOocJ2F5KYabvuw.run(Unknown Source:2)
        at com.android.quickstep.RecentsAnimationDeviceState.runOnUserUnlocked(RecentsAnimationDeviceState.java:272)
        at com.android.quickstep.TouchInteractionService.onCreate(TouchInteractionService.java:301)
        at android.app.ActivityThread.handleCreateService(ActivityThread.java:4186)
        at android.app.ActivityThread.access$1500(ActivityThread.java:237) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1932) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:223) 
        at android.app.ActivityThread.main(ActivityThread.java:7664) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 

packages/apps/Launcher3/quickstep/src/com/android/quickstep/OverviewComponentObserver.java

    public OverviewComponentObserver(Context context, RecentsAnimationDeviceState deviceState) {
    
    
        mContext = context;
        mDeviceState = deviceState;
        mCurrentHomeIntent = new Intent(Intent.ACTION_MAIN)
                .addCategory(Intent.CATEGORY_HOME)
                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        //异常位置: 由于前面注释掉了自带Launcher导致, 将mContext.getPackageName 替换成 三方Launcher的包名可解决.
        mMyHomeIntent = new Intent(mCurrentHomeIntent).setPackage(/*mContext.getPackageName()*/"com.android.launcherX");
        ResolveInfo info = context.getPackageManager().resolveActivity(mMyHomeIntent, 0);
        ComponentName myHomeComponent =
                new ComponentName(/*mContext.getPackageName()*/"com.android.launcherX", info.activityInfo.name);
        mMyHomeIntent.setComponent(myHomeComponent);
        mConfigChangesMap.append(myHomeComponent.hashCode(), info.activityInfo.configChanges);
        ComponentName fallbackComponent = new ComponentName(mContext, RecentsActivity.class);
        mFallbackIntent = new Intent(Intent.ACTION_MAIN)
                .addCategory(Intent.CATEGORY_DEFAULT)
                .setComponent(fallbackComponent)
                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        try {
    
    
            ActivityInfo fallbackInfo = context.getPackageManager().getActivityInfo(
                    mFallbackIntent.getComponent(), 0 /* flags */);
            mConfigChangesMap.append(fallbackComponent.hashCode(), fallbackInfo.configChanges);
        } catch (PackageManager.NameNotFoundException ignored) {
    
     /* Impossible */ }

        mContext.registerReceiver(mUserPreferenceChangeReceiver,
                new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED));
        updateOverviewTargets();
    }

解决奔溃问题后, 点击recent键返回了HOME, 而不是显示RECENT界面:

packages/apps/Launcher3/quickstep/src/com/android/quickstep/OverviewComponentObserver.java


    /**
     * Update overview intent and {@link BaseActivityInterface} based off the current launcher home
     * component.
     */
    private void updateOverviewTargets() {
    
    
        ComponentName defaultHome = PackageManagerWrapper.getInstance()
                .getHomeActivities(new ArrayList<>());

        mIsHomeDisabled = mDeviceState.isHomeDisabled();
        mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome);

        // Set assistant visibility to 0 from launcher's perspective, ensures any elements that
        // launcher made invisible become visible again before the new activity control helper
        // becomes active.
        if (mActivityInterface != null) {
    
    
            mActivityInterface.onAssistantVisibilityChanged(0.f);
        }

        if (SEPARATE_RECENTS_ACTIVITY.get()) {
    
    
            mIsDefaultHome = false;
            if (defaultHome == null) {
    
    
                defaultHome = mMyHomeIntent.getComponent();
            }
        }

        //不走if分支, 
        if (false && !mDeviceState.isHomeDisabled() && (defaultHome == null || mIsDefaultHome)) {
    
    
            // User default home is same as out home app. Use Overview integrated in Launcher.
            mActivityInterface = LauncherActivityInterface.INSTANCE;
            mIsHomeAndOverviewSame = true;
            mOverviewIntent = mMyHomeIntent;
            mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());

            // Remove any update listener as we don't care about other packages.
            unregisterOtherHomeAppUpdateReceiver();
        } else {
    
    
            // The default home app is a different launcher. Use the fallback Overview instead.

            mActivityInterface = FallbackActivityInterface.INSTANCE;
            mIsHomeAndOverviewSame = false;
            mOverviewIntent = mFallbackIntent;
            mCurrentHomeIntent.setComponent(defaultHome);

            // User's default home app can change as a result of package updates of this app (such
            // as uninstalling the app or removing the "Launcher" feature in an update).
            // Listen for package updates of this app (and remove any previously attached
            // package listener).
            if (defaultHome == null) {
    
    
                unregisterOtherHomeAppUpdateReceiver();
            } else if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) {
    
    
                unregisterOtherHomeAppUpdateReceiver();

                mUpdateRegisteredPackage = defaultHome.getPackageName();
                mContext.registerReceiver(mOtherHomeAppUpdateReceiver, getPackageFilter(
                        mUpdateRegisteredPackage, ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED,
                        ACTION_PACKAGE_REMOVED));
            }
        }
        mOverviewChangeListener.accept(mIsHomeAndOverviewSame);
    }

关键在于使mOverviewIntent = mFallbackIntent;

由SystemUI调用onOverviewToggle:

packages/apps/Launcher3/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java

        @BinderThread
        @Override
        public void onOverviewToggle() {
    
    
            TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
            mOverviewCommandHelper.onOverviewToggle();
        }

packages/apps/Launcher3/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java

    @BinderThread
    public void onOverviewToggle() {
    
    
        // If currently screen pinning, do not enter overview
        if (mDeviceState.isScreenPinningActive()) {
    
    
            return;
        }

        ActivityManagerWrapper.getInstance()
                .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
        MAIN_EXECUTOR.execute(new RecentsActivityCommand<>());
    }

MAIN_EXECUTOR 和 LooperExecutor

AbstractExecutorService
LooperExecutor
Executors
LooperExecutor MAIN_EXECUTOR

RecentsActivityCommand

    private class RecentsActivityCommand<T extends StatefulActivity<?>> implements Runnable {
    
    
        protected final BaseActivityInterface<?, T> mActivityInterface;
        //.....
        private ActivityInitListener mListener;

        public RecentsActivityCommand() {
    
    
            mActivityInterface = mOverviewComponentObserver.getActivityInterface();
			//....
        }
        @Override
        public void run() {
    
    
			//...
            // Otherwise, start overview.
            mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);
            mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
                    new RemoteAnimationProvider() {
    
    
                        @Override
                        public AnimatorSet createWindowAnimation(
                                RemoteAnimationTargetCompat[] appTargets,
                                RemoteAnimationTargetCompat[] wallpaperTargets) {
    
    
                            return RecentsActivityCommand.this.createWindowAnimation(appTargets,
                                    wallpaperTargets);
                        }
                    }, mContext, MAIN_EXECUTOR.getHandler(),
                    mAnimationProvider.getRecentsLaunchDuration());
        } 

packages/apps/Launcher3/quickstep/src/com/android/quickstep/OverviewComponentObserver.java

    /**
     * Get the current activity control helper for managing interactions to the overview activity.
     *
     * @return the current activity control helper
     */
    public BaseActivityInterface getActivityInterface() {
    
    
        return mActivityInterface;
    }

OverviewComponentObserver.mActivityInterface = FallbackActivityInterface.INSTANCE;

packages/apps/Launcher3/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java

public final class FallbackActivityInterface extends
        BaseActivityInterface<RecentsState, RecentsActivity> {
    
    

    public static final FallbackActivityInterface INSTANCE = new FallbackActivityInterface();

    @Override
    public ActivityInitListener createActivityInitListener(
            Predicate<Boolean> onInitListener) {
    
    
        return new ActivityInitListener<>((activity, alreadyOnHome) ->
                onInitListener.test(alreadyOnHome), RecentsActivity.ACTIVITY_TRACKER);
    }

packages/apps/Launcher3/quickstep/src/com/android/quickstep/util/ActivityInitListener.java

    /**
     * Starts the given intent with the provided animation. Unlike {@link #register(Intent)}, this
     * method will not call {@link #init} if the activity already exists, it will only call it when
     * we get handleIntent() for the provided intent that we're starting.
     */
    public void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
            Context context, Handler handler, long duration) {
    
    
        mIsRegistered = true;

        Bundle options = animProvider.toActivityOptions(handler, duration, context).toBundle();
        context.startActivity(addToIntent(new Intent(intent)), options);
    }

最终context.startActivity 其中的Intent是:mOverviewComponentObserver.getOverviewIntent()

packages/apps/Launcher3/quickstep/src/com/android/quickstep/OverviewComponentObserver.java

    /**
     * Get the current intent for going to the overview activity.
     *
     * @return the overview intent
     */
    public Intent getOverviewIntent() {
    
    
    	/*参见前面的代码:
    	ComponentName fallbackComponent = new ComponentName(mContext, RecentsActivity.class);
        mFallbackIntent = new Intent(Intent.ACTION_MAIN)
                .addCategory(Intent.CATEGORY_DEFAULT)
                .setComponent(fallbackComponent)
                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         */
        return mOverviewIntent;
    }
1. getActivityInterface
2.createActivityInitListener
3.getOverviewIntent
4.registerAndStartActivity
RecentsActivityCommand
OverviewComponentObserver
FallbackActivityInterface
ActivityInitListener
void registerAndStartActivity()

至此RecentsActivity启动完成.

模块编译:

mmm packages/apps/Launcher3/:Launcher3QuickStep -j4

需要注意的是

  1. 原生的Launcher3需要注释掉, 或让它不参与编译.
  2. 近期任务UI的存放目录是在Lauuncher3目录下, 而需要参与编译的模块只有Launcher3QuickStep
  3. 第三方Launcher不能使用com.android.launcher3这个包名, 与上面的模块冲突

参考

SystemUI之QuickStep探索(常规启动篇)
Android11 最近任务Recents功能分析

猜你喜欢

转载自blog.csdn.net/ansondroider/article/details/126342245