Android 11 SystemUI 启动流程

这篇具有很好参考价值的文章主要介绍了Android 11 SystemUI 启动流程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

SystemUI 有哪内容

从表面上看, 我们看到的状态栏、通知栏、下拉菜单、导航栏、锁屏、最近任务、低电提示等系统页面都是 SystemUI 的。SystemUI,在源码目录中位于: framework/base/packages 目录下, 可见 SystemUI 和 framework 是关联的, SystemUI 依赖了很多内部 API , 系统资源, SystemUI 编译是要依赖系统源码的。

SystemUI 也是一个应用,不过这个应用特殊之处在于他没有启动图标、也没有入口 Activity 。他的入口程序是一个服务:SystemUIService。 这个服务会被系统服务拉起来, 这个服务起来, SystemUI 应用进程就创建起来了,具体启动过程后面会分析。除了 SystemUIService , SystemUI 还有很多服务, 例如: 负责锁屏的KeyguardService、负责最近任务的 RecentsSystemUserService、负责壁纸的 ImageWallpaper 、负责截屏的TakeScreenshotService 等。

Android 11 SystemUI 启动流程,Android Framework,android

下面是PhoneStatusBarView的view 树形图:

Android 11 SystemUI 启动流程,Android Framework,android

PanelHolder

PanelHolder是用户下拉 status bar 后得到的 view。它主要包含 QuickSettings 和 Notification panel 两个部分。PanelHolder是一个继承自FrameLayout的自定义view,它的内容是通过include status_bar_expanded.xml进行填充的。PanelHolder的布局比较复杂,为了提高view的重用性大量的使用了include标签。下面是PanelHolder的view树形图, 只给出了了主要的view:

Android 11 SystemUI 启动流程,Android Framework,android

架构关系

在系统服务中,有一个服务是专门为 SystemUI 的状态栏服务的, 这个服务就是 StatusbarManagerService (简称:SMS),和这个服务关系比较密切的服务是 WindowManagerService(简称:WMS), SMS 主要管控的是状态栏、导航栏, 例如:我们可以设置全屏、沉浸式状态栏都是 SMS 在起作用。

Android 11 SystemUI 启动流程,Android Framework,android

services组件启动时配置列表 : (R.array.config_systemUIServiceComponents)

所有 SystemUIService 都是继承自 SystemUI.class , SystemUI.class 是一个抽象类

<item>com.android.systemui.util.NotificationChannels</item> 通知信息 
<item>com.android.systemui.keyguard.KeyguardViewMediator</item> 锁屏 
<item>com.android.systemui.recents.Recents</item> 近期列表
Android 10之后近期列表的显示被移到Launcher里面了。在Launcher3的一个 类中TouchInteractionService.java   IBinder mMyBinder = new IOverviewProxy.Stub() 通过AIDL的方法与systemUI通信
————————————————
<item>com.android.systemui.volume.VolumeUI</item> 声音UI显示 
<item>com.android.systemui.statusbar.phone.StatusBar</item> 状态栏及下拉面板
<item>com.android.systemui.usb.StorageNotification</item> usb通知管理 
<item>com.android.systemui.power.PowerUI</item>  电源UI显示管理
<item>com.android.systemui.media.RingtonePlayer</item> 播放铃声 
<item>com.android.systemui.keyboard.KeyboardUI</item>键盘UI 
<item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>快捷方式  
<item>@string/config_systemUIVendorServiceComponent</item>厂商相关定制 
<item>com.android.systemui.util.leak.GarbageMonitor$Service</item>垃圾监测器  
<item>com.android.systemui.LatencyTester</item> 延迟测试仪 
<item>com.android.systemui.globalactions.GlobalActionsComponent</item>  关机界面的显示、全局控制
<item>com.android.systemui.ScreenDecorations</item>屏幕装饰  
<item>com.android.systemui.biometrics.AuthController</item>生物识别  
<item>com.android.systemui.SliceBroadcastRelayHandler</item> 切片广播 
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>  
<item>com.android.systemui.theme.ThemeOverlayController</item>  
<item>com.android.systemui.accessibility.WindowMagnification</item>  
<item>com.android.systemui.accessibility.SystemActions</item>  
<item>com.android.systemui.toast.ToastUI</item>  Toast
<item>com.android.systemui.wmshell.WMShell</item> 

一、SystemUI的启动流程

1、SystemServer

SystemServer启动后,会在Main Thread启动ActivityManagerService,当ActivityManagerService systemReady后,会去启动SystemUIService。

/frameworks/base/services/java/com/android/server/SystemServer.java

①main

    /**
     * The main entry point from zygote.
     */
    public static void main(String[] args) {
        new SystemServer().run();
    }

②run

    private void run() {
        t.traceBegin("InitBeforeStartServices");
        ....
       // Create the system service manager.
            mSystemServiceManager = new SystemServiceManager(mSystemContext);
            mSystemServiceManager.setStartInfo(mRuntimeRestart,
                    mRuntimeStartElapsedTime, mRuntimeStartUptime);
            LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
        ....
       }

③mActivityManagerService.systemReady

mActivityManagerService.systemReady(() -> {
    //准备好服务
    Slog.i(TAG, "Making services ready");
    ....
            //跟踪开启系统界面
            t.traceBegin("StartSystemUI");
            try {
                //开启系统界面
                startSystemUi(context, windowManagerF);
            } catch (Throwable e) {
                reportWtf("starting System UI", e);
            }
            t.traceEnd();
    ....
}

④startSystemUi

    private static void startSystemUi(Context context, WindowManagerService windowManager) {
        PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
        Intent intent = new Intent();
        intent.setComponent(pm.getSystemUiServiceComponent());
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        //Slog.d(TAG, "Starting service: " + intent);
        //通过startServiceAsUser,SystemUIService就启动了,即SystemUI进程开机启动
        context.startServiceAsUser(intent, UserHandle.SYSTEM);
        windowManager.onSystemUiStarted();
    }

2、systemUIService

在SystemUIService的onCreate方法中会调用SystemUIApplication的startServicesIfNeeded方法,这个方法会调用 startServicesIfNeeded(SERVICES)方法启动一系列服务

    @Override
    public void onCreate() {
        super.onCreate();

        // Start all of SystemUI
        ((SystemUIApplication) getApplication()).startServicesIfNeeded();

3、SystemUIApplication

/frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java

①startServicesIfNeeded()

public void startServicesIfNeeded() {
    //获取所有的服务的路径,所有SERVICES统一继承了SystemUI类:
    String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());
    startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);
}

②startServicesIfNeeded(String metricsPrefix, String[] services)

在重载方法中将每一个名称通过反射来得到实例对象,然后依次调用每一个SystemUI的子类的start方法启动每一个模块。

private void startServicesIfNeeded(String metricsPrefix, String[] services) {
    if (mServicesStarted) {
        return;
    }
    mServices = new SystemUI[services.length];

    //检查一下,也许在我们开始之前很久它就已经完成了
    if (!mBootCompleteCache.isBootComplete()) {
        // check to see if maybe it was already completed long before we began
        // see ActivityManagerService.finishBooting()
        if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
            mBootCompleteCache.setBootComplete();
            if (DEBUG) {
                Log.v(TAG, "BOOT_COMPLETED was already sent");
            }
        }
    }

    final DumpManager dumpManager = mRootComponent.createDumpManager();

    Log.v(TAG, "Starting SystemUI services for user " +
            Process.myUserHandle().getIdentifier() + ".");
    TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
            Trace.TRACE_TAG_APP);
    //开始追踪
    log.traceBegin(metricsPrefix);
    final int N = services.length;
    //遍历services这个数组
    for (int i = 0; i < N; i++) {
        String clsName = services[i];
        if (DEBUG) Log.d(TAG, "loading: " + clsName);
        log.traceBegin(metricsPrefix + clsName);
        long ti = System.currentTimeMillis();
        try {
            SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
            if (obj == null) {
                Constructor constructor = Class.forName(clsName).getConstructor(Context.class);
                obj = (SystemUI) constructor.newInstance(this);
            }
            mServices[i] = obj;
        } catch (ClassNotFoundException
                | NoSuchMethodException
                | IllegalAccessException
                | InstantiationException
                | InvocationTargetException ex) {
            throw new RuntimeException(ex);
        }

        if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
        //依次调用service的start方法启动服务
        mServices[i].start();
        log.traceEnd();

        // Warn if initialization of component takes too long
        //如果组件初始化时间过长,则发出警告
        ti = System.currentTimeMillis() - ti;
        if (ti > 1000) {
            Log.w(TAG, "Initialization of " + clsName + " took " + ti + " ms");
        }
        if (mBootCompleteCache.isBootComplete()) {
            mServices[i].onBootCompleted();
        }

        dumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
    }
    mRootComponent.getInitController().executePostInitTasks();
    //结束追踪
    log.traceEnd();

    mServicesStarted = true;
}

4、SystemUI

/**
 * @see SystemUIApplication#startServicesIfNeeded()
 *系统界面应用  如果需要,启动服务
 */
public abstract class SystemUI implements Dumpable {
    protected final Context mContext;

    public SystemUI(Context context) {
        mContext = context;
    }

    public abstract void start();

    protected void onConfigurationChanged(Configuration newConfig) {
    }

    @Override
    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
    }

    protected void onBootCompleted() {
    }

    public static void overrideNotificationAppName(Context context, Notification.Builder n,
            boolean system) {
        final Bundle extras = new Bundle();
        String appName = system
                ? context.getString(com.android.internal.R.string.notification_app_name_system)
                : context.getString(com.android.internal.R.string.notification_app_name_settings);
        extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, appName);

        n.addExtras(extras);
    }
}

二、状态栏

1、SystemBars

SystemBars加载基本全部SystemUI的界面显示,由前面可知调用的start方法实际上是每一个继承于SytemUI的子类中的方法

①start

Android 11 SystemUI 启动流程,Android Framework,android

②createStatusBarFromConfig

从string资源文件里面读取class name,通过java的反射机制实例化对象,然后调用start()方法启动,class name的值如下图:

    private void createStatusBarFromConfig() {
        ......
        String clsName = mContext.getString(R.string.config_statusBarComponent);
        ......
        try {
            cls = mContext.getClassLoader().loadClass(clsName);
        } catch (Throwable t) {
            throw andLog("Error loading status bar component: " + clsName, t);
        }
        try {
            mStatusBar = (BaseStatusBar) cls.newInstance();
        } catch (Throwable t) {
            throw andLog("Error creating status bar component: " + clsName, t);
        }
        ......
        mStatusBar.start();
        ......
    }

③String

    <!-- Component to be used as the status bar service.  Must implement the IStatusBar
     interface.  This name is in the ComponentName flattened format (package/class)  -->
    <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>

2、StatusBar

①createAndAddWindows

    public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
        //创建状态栏
        makeStatusBarView(result);
        mNotificationShadeWindowController.attach();
        //创建状态栏的窗口
        mStatusBarWindowController.attach();
    }

②makeStatusBarView

protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
    final Context context = mContext;
    .....
    FragmentHostManager.get(mPhoneStatusBarWindow)
            .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
                //CollapsedStatusBarFragment 替换 status_bar_container(状态栏通知显示区域)
                CollapsedStatusBarFragment statusBarFragment =
                        (CollapsedStatusBarFragment) fragment;

                PhoneStatusBarView oldStatusBarView = mStatusBarView;
                mStatusBarView = (PhoneStatusBarView) statusBarFragment.getView();
                //传递statusBar处理下拉事件
                mStatusBarView.setBar(this);
                //传递 NotificationPanelView 显示下拉UI控制
                mStatusBarView.setPanel(mNotificationPanelViewController);
                mStatusBarView.setScrimController(mScrimController);
                //初始化通知栏区域
                statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
                ......
                    
            }).getFragmentManager()
            .beginTransaction()
            .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
                    CollapsedStatusBarFragment.TAG)
            .commit();
    .....

2、CollapsedStatusBarFragment

①onCreateView

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
            Bundle savedInstanceState) {
        return inflater.inflate(R.layout.status_bar, container, false);
    }

②initNotificationIconArea

public void initNotificationIconArea(NotificationIconAreaController
        notificationIconAreaController) {
    //notification_icon_area是在status_bar.xml的布局,它是属于通知Notification    
    //获取到 notification_icon_area,FrameLayout转为ViewGroup,
    ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);
    //调用 notificationIconAreaController 获取通知要显示的view(LinearLayout)
    //在4中跟进
    mNotificationIconAreaInner =
            notificationIconAreaController.getNotificationInnerAreaView();
    //如果已经有显示的view,通过 view 父布局将其自身remove,然后再重新addView。
    //最后将 mNotificationIconAreaInner 显示出来(设置透明度为1,visibility为VISIBLE)
    if (mNotificationIconAreaInner.getParent() != null) {
        ((ViewGroup) mNotificationIconAreaInner.getParent())
                .removeView(mNotificationIconAreaInner);
    }
    notificationIconArea.addView(mNotificationIconAreaInner);

    //与上面一样
    ViewGroup statusBarCenteredIconArea = mStatusBar.findViewById(R.id.centered_icon_area);
    mCenteredIconArea = notificationIconAreaController.getCenteredNotificationAreaView();
    if (mCenteredIconArea.getParent() != null) {
        ((ViewGroup) mCenteredIconArea.getParent())
                .removeView(mCenteredIconArea);
    }
    statusBarCenteredIconArea.addView(mCenteredIconArea);

    //默认为显示,直到我们知道其他情况
    // Default to showing until we know otherwise.
    showNotificationIconArea(false);
}

③showNotificationIconArea

当状态栏下拉时,状态栏中的图标icon会慢慢的变成透明和不可见,就是通过hideSystemIconArea(true), hideNotificationIconArea(true)

//当状态栏下拉时,设置状态栏中的图标icon会慢慢的变成透明和不可见    
public void hideNotificationIconArea(boolean animate) {
        animateHide(mNotificationIconAreaInner, animate);
        animateHide(mCenteredIconArea, animate);
    }
//设置状态栏图标透明度为1,visibility为VISIBLE
    public void showNotificationIconArea(boolean animate) {
        animateShow(mNotificationIconAreaInner, animate);
        animateShow(mCenteredIconArea, animate);
    }

    public void hideOperatorName(boolean animate) {
        if (mOperatorNameFrame != null) {
            animateHide(mOperatorNameFrame, animate);
        }
    }

    public void showOperatorName(boolean animate) {
        if (mOperatorNameFrame != null) {
            animateShow(mOperatorNameFrame, animate);
        }

④animateShow

    private void animateShow(View v, boolean animate) {
        v.animate().cancel();
        //(设置透明度为1,visibility为VISIBLE)
        v.setVisibility(View.VISIBLE);
        if (!animate) {
            v.setAlpha(1f);
            return;
        }
        .....
    }

⑤animateHiddenState

//将视图动画化为 INVISIBLE 或 GONE    
private void animateHiddenState(final View v, int state, boolean animate) {
        v.animate().cancel();
        if (!animate) {
            v.setAlpha(0f);
            v.setVisibility(state);
            return;
        }

        v.animate()
                .alpha(0f)
                .setDuration(160)
                .setStartDelay(0)
                .setInterpolator(Interpolators.ALPHA_OUT)
                .withEndAction(() -> v.setVisibility(state));
    }

3、status_bar.xml

<?xml version="1.0" encoding="utf-8"?>
<!--
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->

<!--    android:background="@drawable/status_bar_closed_default_background" -->
<com.android.systemui.statusbar.phone.PhoneStatusBarView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
    android:layout_width="match_parent"
    android:layout_height="@dimen/status_bar_height"
    android:id="@+id/status_bar"
    android:orientation="vertical"
    android:focusable="false"
    android:descendantFocusability="afterDescendants"
    android:accessibilityPaneTitle="@string/status_bar"
    >

    <!-- add for KGDAANWIKFRA-135 -->
    <View
        android:layout_width="match_parent"
        android:layout_height="@dimen/status_bar_height"
        android:id="@+id/status_bar_dark_view"
        android:background="#ff000000"
        android:visibility="gone" />
    //<!--通知灯,默认gone-->
    <ImageView
        android:id="@+id/notification_lights_out"
        android:layout_width="@dimen/status_bar_icon_size"
        android:layout_height="match_parent"
        android:paddingStart="@dimen/status_bar_padding_start"
        android:paddingBottom="2dip"
        android:src="@drawable/ic_sysbar_lights_out_dot_small"
        android:scaleType="center"
        android:visibility="gone"
        />
    //<!--状态栏内容-->
    <LinearLayout android:id="@+id/status_bar_contents"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingStart="@dimen/status_bar_padding_start"
        android:paddingEnd="@dimen/status_bar_padding_end"
        android:paddingTop="@dimen/status_bar_padding_top"
        android:orientation="horizontal"
        >
        <FrameLayout
            android:layout_height="match_parent"
            android:layout_width="0dp"
            android:layout_weight="1">

            <include layout="@layout/heads_up_status_bar_layout" />

            <!-- The alpha of the left side is controlled by PhoneStatusBarTransitions, and the
             individual views are controlled by StatusBarManager disable flags DISABLE_CLOCK and
             DISABLE_NOTIFICATION_ICONS, respectively -->
            <LinearLayout
                android:id="@+id/status_bar_left_side"
                android:layout_height="match_parent"
                android:layout_width="match_parent"
                android:clipChildren="false"
            >
                <ViewStub
                    android:id="@+id/operator_name"
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:layout="@layout/operator_name" />

                <com.android.systemui.statusbar.policy.Clock
                    android:id="@+id/clock"
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:textAppearance="@style/TextAppearance.StatusBar.Clock"
                    android:singleLine="true"
                    android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
                    android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
                    android:gravity="center_vertical|start"
                />

                //<!--通知图标区域-->
                <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
                    android:id="@+id/notification_icon_area"
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:orientation="horizontal"
                    android:clipChildren="false"/>

            </LinearLayout>
        </FrameLayout>

        <!-- Space should cover the notch (if it exists) and let other views lay out around it -->
        <android.widget.Space
            android:id="@+id/cutout_space_view"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:gravity="center_horizontal|center_vertical"
        />

        //居中的图标区域        
        <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
            android:id="@+id/centered_icon_area"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:clipChildren="false"
            android:gravity="center_horizontal|center_vertical"/>

        <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="horizontal"
            android:gravity="center_vertical|end"
            >
            //<!--系统图标-->
            <include layout="@layout/system_icons" />
        </com.android.keyguard.AlphaOptimizedLinearLayout>
    </LinearLayout>
    //<!--紧急密码管理员文本-->
    <ViewStub
        android:id="@+id/emergency_cryptkeeper_text"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout="@layout/emergency_cryptkeeper_text"
    />

</com.android.systemui.statusbar.phone.PhoneStatusBarView>

4、NotificationIconAreaController

①getNotificationInnerAreaView

    /**
     * Returns the view that represents the notification area.+
     * 返回表示通知区域的视图。
     */
    public View getNotificationInnerAreaView() {
        return mNotificationIconArea;
    }

②initializeNotificationAreaViews

/**
 * Initializes the views that will represent the notification area.
 * 初始化将表示通知区域的视图。
 */
protected void initializeNotificationAreaViews(Context context) {
    reloadDimens(context);
    
    LayoutInflater layoutInflater = LayoutInflater.from(context);
    //通知图标区域布局
    mNotificationIconArea = inflateIconArea(layoutInflater);
    //通知图标
    mNotificationIcons = mNotificationIconArea.findViewById(R.id.notificationIcons);
    //获取通知滚动布局
    mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout();
    //中心图标区域布局
    mCenteredIconArea = layoutInflater.inflate(R.layout.center_icon_area, null);
    //居中的图标
    mCenteredIcon = mCenteredIconArea.findViewById(R.id.centeredIcon);

    initAodIcons();
}

③inflateIconArea

    protected View inflateIconArea(LayoutInflater inflater) {
        return inflater.inflate(R.layout.notification_icon_area, null);
    }

三、status icon加载流程

1、 status_bar.xml

        <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
            android:id="@+id/centered_icon_area"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:clipChildren="false"
            android:gravity="center_horizontal|center_vertical"/>

        <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="horizontal"
            android:gravity="center_vertical|end"
            >
            <!--系统图标-->
            <include layout="@layout/system_icons" />
        </com.android.keyguard.AlphaOptimizedLinearLayout>

2、system_icons.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:id="@+id/system_icons"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_vertical">
    //StatusIconContainer继承AlphaOptimizedLinearLayout
    <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:paddingEnd="@dimen/signal_cluster_battery_padding"
        android:gravity="center_vertical"
        android:orientation="horizontal"/>

    <com.android.systemui.BatteryMeterView android:id="@+id/battery"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:clipToPadding="false"
        android:clipChildren="false"
        systemui:textAppearance="@style/TextAppearance.StatusBar.Clock" />
</LinearLayout>

3、AlphaOptimizedLinearLayout

    //该方法用来标记当前view是否存在过度绘制,存在返回ture,不存在返回false,
    //api里面默认返回为true,status icon不存在过度绘制。
    @Override
    public boolean hasOverlappingRendering() {
        return false;
    }

4、CollapsedStatusBarFragment

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mStatusBar = (PhoneStatusBarView) view;
        if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {
            mStatusBar.restoreHierarchyState(
                    savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));
        }
......
    }

4、StatusBarIconController

①DarkIconManager

    /**
     * Version of ViewGroup that observes state from the DarkIconDispatcher.
     */
    public static class DarkIconManager extends IconManager {
        private final DarkIconDispatcher mDarkIconDispatcher;
        private int mIconHPadding;

        public DarkIconManager(LinearLayout linearLayout, CommandQueue commandQueue) {
            super(linearLayout, commandQueue);
            mIconHPadding = mContext.getResources().getDimensionPixelSize(
                    R.dimen.status_bar_icon_padding);
            mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
        }
        //每个icon应该就是对应着代表顺序的index和数据类型为String的slot
        @Override
        protected void onIconAdded(int index, String slot, boolean blocked,
                StatusBarIconHolder holder) {
            StatusIconDisplayable view = addHolder(index, slot, blocked, holder);
            mDarkIconDispatcher.addDarkReceiver((DarkReceiver) view);
        }
        .....

        //onSetIcon可能就是刷新icon状态的
        @Override
        public void onSetIcon(int viewIndex, StatusBarIcon icon) {
            super.onSetIcon(viewIndex, icon);
            mDarkIconDispatcher.applyDark((DarkReceiver) mGroup.getChildAt(viewIndex));
        }
        .....

    }

②IconManager

public static class IconManager implements DemoMode {
    .....
    protected void onIconAdded(int index, String slot, boolean blocked,
        StatusBarIconHolder holder) {
        
        addHolder(index, slot, blocked, holder);
    }    
    
    protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked,
        StatusBarIconHolder holder) {
        switch (holder.getType()) {
            case TYPE_ICON:
                return addIcon(index, slot, blocked, holder.getIcon());
    
            case TYPE_WIFI:
                return addSignalIcon(index, slot, holder.getWifiState());
    
            case TYPE_MOBILE:
                return addMobileIcon(index, slot, holder.getMobileState());
        }
        return null;
    }
    
    @VisibleForTesting
    protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
            StatusBarIcon icon) {
        
            StatusBarIconView view = onCreateStatusBarIconView(slot, blocked);
            view.set(icon);
            mGroup.addView(view, index, onCreateLayoutParams());
            return view;
    }
        .....
}

5、StatusBarIconControllerImpl

//继承StatusBarIconList
public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable,
        ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController {   
    ......        
    @Inject
    public StatusBarIconControllerImpl(Context context, CommandQueue commandQueue) {
        //config_statusBarIcons
        super(context.getResources().getStringArray(
                com.android.internal.R.array.config_statusBarIcons));
        Dependency.get(ConfigurationController.class).addCallback(this);
     .....
    }
}

6、StatusBarIconList

在初始化的时候就已经定义好了所有的slots,然后从framework中加载出来,index就是string-array中的顺序。

public class StatusBarIconList {
    private ArrayList<Slot> mSlots = new ArrayList<>();

    public StatusBarIconList(String[] slots) {
        final int N = slots.length;
        for (int i=0; i < N; i++) {
            mSlots.add(new Slot(slots[i], null));
        }
    }

7、config_statusBarIcons

<string-array name="config_statusBarIcons">
        <item><xliff:g id="id">@string/status_bar_rotate</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_headset</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_data_saver</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_ime</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_sync_failing</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_sync_active</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_location</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_nfc</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_tty</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_speakerphone</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_zen</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_mute</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_volume</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_ethernet</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_wifi</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_cdma_eri</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_data_connection</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_phone_evdo_signal</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_phone_signal</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_alarm_clock</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_secure</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_clock</xliff:g></item>
    </string-array>
 
    <string translatable="false" name="status_bar_rotate">rotate</string>
    <string translatable="false" name="status_bar_headset">headset</string>
    <string translatable="false" name="status_bar_data_saver">data_saver</string>
    <string translatable="false" name="status_bar_managed_profile">managed_profile</string>
    <string translatable="false" name="status_bar_ime">ime</string>
    <string translatable="false" name="status_bar_sync_failing">sync_failing</string>
    <string translatable="false" name="status_bar_sync_active">sync_active</string>
    <string translatable="false" name="status_bar_cast">cast</string>
    <string translatable="false" name="status_bar_hotspot">hotspot</string>
    <string translatable="false" name="status_bar_location">location</string>
    <string translatable="false" name="status_bar_bluetooth">bluetooth</string>
    <string translatable="false" name="status_bar_nfc">nfc</string>
    <string translatable="false" name="status_bar_tty">tty</string>
    <string translatable="false" name="status_bar_speakerphone">speakerphone</string>
    <string translatable="false" name="status_bar_zen">zen</string>
    <string translatable="false" name="status_bar_mute">mute</string>
    <string translatable="false" name="status_bar_volume">volume</string>
    <string translatable="false" name="status_bar_wifi">wifi</string>
    <string translatable="false" name="status_bar_cdma_eri">cdma_eri</string>
    <string translatable="false" name="status_bar_data_connection">data_connection</string>
    <string translatable="false" name="status_bar_phone_evdo_signal">phone_evdo_signal</string>
    <string translatable="false" name="status_bar_phone_signal">phone_signal</string>
    <string translatable="false" name="status_bar_battery">battery</string>
    <string translatable="false" name="status_bar_alarm_clock">alarm_clock</string>
    <string translatable="false" name="status_bar_secure">secure</string>
    <string translatable="false" name="status_bar_clock">clock</string>
    <string translatable="false" name="status_bar_mobile">mobile</string>
    <string translatable="false" name="status_bar_vpn">vpn</string>
    <string translatable="false" name="status_bar_ethernet">ethernet</string>
    <string translatable="false" name="status_bar_airplane">airplane</string>

好了,到这里我们的第一部分初始化流程就讲完了

四、状态显示流程

由上面的初始化流程我们可以知道,每个icon都对应了slot,slot数量比较多,我们就挑一个常见的Headset讲下,其他的流程都是大致一样的。

1、PhoneStatusBarPolicy

初始化注册了大量的监听

①init

//  初始化headset的slot
mSlotHeadset = resources.getString(com.android.internal.R.string.status_bar_headset);
/** Initialize the object after construction. */
public void init() {
    // listen for broadcasts
    IntentFilter filter = new IntentFilter();
    //  注册headset状态变化的action
    filter.addAction(AudioManager.ACTION_HEADSET_PLUG);
    
    filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
    filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
    filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
    filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
    filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
    mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler);
    Observer<Integer> observer = ringer -> mHandler.post(this::updateVolumeZen);

    mRingerModeTracker.getRingerMode().observeForever(observer);
    mRingerModeTracker.getRingerModeInternal().observeForever(observer);
    ....
    }

②BroadcastReceiver

    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            switch (action) {
                case Intent.ACTION_SIM_STATE_CHANGED:
                    // Avoid rebroadcast because SysUI is direct boot aware.
                    if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
                        break;
                    }
                    break;
                case TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED:
                    updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,
                            TelecomManager.TTY_MODE_OFF));
                    break;
                case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
                case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
                case Intent.ACTION_MANAGED_PROFILE_REMOVED:
                    updateManagedProfile();
                    break;
                //监听ACTION_HEADSET_PLUG
                case AudioManager.ACTION_HEADSET_PLUG:
                    updateHeadsetPlug(context, intent);
                    break;
            }
        }
    };

③updateHeadsetPlug

完成icon添加和状态监听,然后当收到对应的action变化的时候,更新headset icon

    private void updateHeadsetPlug(Context context, Intent intent) {
        boolean connected = intent.getIntExtra("state", 0) != 0;
        boolean hasMic = intent.getIntExtra("microphone", 0) != 0;
        if (connected) {
            String contentDescription = mResources.getString(hasMic
                    ? R.string.accessibility_status_bar_headset
                    : R.string.accessibility_status_bar_headphones);
            //setIcon负责设置icon
            mIconController.setIcon(mSlotHeadset, hasMic ? R.drawable.stat_sys_headset_mic
                    : R.drawable.stat_sys_headset, contentDescription);
            //setIconVisibility则根据connected状态设置icon的可见性
            mIconController.setIconVisibility(mSlotHeadset, true);
        } else {
            /*UNISOC: Add for bug 1130932 {@ */
            AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
            if (!audioManager.isWiredHeadsetOn()) {
                //setIconVisibility则根据connected状态设置icon的可见性
                mIconController.setIconVisibility(mSlotHeadset, false);
            }
            /* @} */
        }
    }

2、StatusBarIconControllerImpl

①setIcon

@Override
public void setIcon(String slot, int resourceId, CharSequence contentDescription) {
    //根据slot找到对应的index
    int index = getSlotIndex(slot);
    //用index取得对应的icon
    StatusBarIconHolder holder = getIcon(index, 0);
    if (holder == null) {
        //第一次的时候,icon == null,所以通过new StatusBarIcon创建一个
        StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
                Icon.createWithResource(
                        mContext, resourceId), 0, 0, contentDescription);
        holder = StatusBarIconHolder.fromIcon(icon);
        //然后调用此方法
        setIcon(index, holder);
    } else {
        holder.getIcon().icon = Icon.createWithResource(mContext, resourceId);
        holder.getIcon().contentDescription = contentDescription;
        handleSet(index, holder);
    }
}

@Override
public void setIcon(int index, @NonNull StatusBarIconHolder holder) {
    boolean isNew = getIcon(index, holder.getTag()) == null;
    super.setIcon(index, holder);

    if (isNew) {
        addSystemIcon(index, holder);
    } else {
        handleSet(index, holder);
    }
}

private void addSystemIcon(int index, StatusBarIconHolder holder) {
    String slot = getSlotName(index);
    int viewIndex = getViewIndex(index, holder.getTag());
    boolean blocked = mIconBlacklist.contains(slot);
    //onIconAdded-》初始化流程里面StatusBarIconController中添加icon的入口
    //到这里setIcon就添加完毕了
    mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, holder));
}

②setIconVisibility

public void setIconVisibility(String slot, boolean visibility) {
    int index = getSlotIndex(slot);
    StatusBarIconHolder holder = getIcon(index, 0);
    if (holder == null || holder.isVisible() == visibility) {
        return;
    }
    holder.setVisible(visibility);
    //icon已经创建成功了,icon非空,并且第一次是icon.visible != visibility的
    //就会顺利的走到handleSet(index, icon)
    handleSet(index, holder);
}

private void handleSet(int index, StatusBarIconHolder holder) {
    int viewIndex = getViewIndex(index, holder.getTag());
    //初始化流程里面StatusBarIconController中setIcon的地方
    //到这里setIconVisibility也设置完毕了
    mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));
}

五、创建状态栏的窗口

1、StatusBarWindowController

mStatusBarWindowController.attach()

    /**
     * Adds the status bar view to the window manager.
     */
    public void attach() {
        // Now that the status bar window encompasses the sliding panel and its
        // translucent backdrop, the entire thing is made TRANSLUCENT and is
        // hardware-accelerated.
        mLp = new WindowManager.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                mBarHeight,
                WindowManager.LayoutParams.TYPE_STATUS_BAR,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                        | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                PixelFormat.TRANSLUCENT);
        mLp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
        mLp.token = new Binder();
        mLp.gravity = Gravity.TOP;
        mLp.setFitInsetsTypes(0 /* types */);
        mLp.setTitle("StatusBar");
        mLp.packageName = mContext.getPackageName();
        mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;

        //WindowManager中添加view
        //mStatusBarView = mSuperStatusBarViewFactory.getStatusBarWindowView();
        //private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
        mWindowManager.addView(mStatusBarView, mLp);
        mLpChanged.copyFrom(mLp);
    }

2、SuperStatusBarViewFactory

    /**
     * Gets the inflated {@link StatusBarWindowView} from {@link R.layout#super_status_bar}.
     * Returns a cached instance, if it has already been inflated.
     */
    public StatusBarWindowView getStatusBarWindowView() {
        if (mStatusBarWindowView != null) {
            return mStatusBarWindowView;
        }

        //由其可知加载的布局来自于super_status_bar
        mStatusBarWindowView =
                (StatusBarWindowView) mInjectionInflationController.injectable(
                LayoutInflater.from(mContext)).inflate(R.layout.super_status_bar,
                /* root= */ null);
        if (mStatusBarWindowView == null) {
            throw new IllegalStateException(
                    "R.layout.super_status_bar could not be properly inflated");
        }
        return mStatusBarWindowView;
    }

3、super_status_bar.xml

从前面可知这里会用CollapsedStatusBarFragment 替换 status_bar_container(状态栏通知显示区域),完成图标的显示

<!-- This is the status bar window. -->
<com.android.systemui.statusbar.phone.StatusBarWindowView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:sysui="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
    >
        <FrameLayout
            android:id="@+id/status_bar_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:visibility="gone"
        />

        <FrameLayout
            android:id="@+id/car_top_navigation_bar_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </LinearLayout>

</com.android.systemui.statusbar.phone.StatusBarWindowView

六、电池图标刷新流程

1、BatteryMeterView

① 构造方法BatteryMeterView()

    public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        BroadcastDispatcher broadcastDispatcher = Dependency.get(BroadcastDispatcher.class);

        setOrientation(LinearLayout.HORIZONTAL);
        setGravity(Gravity.CENTER_VERTICAL | Gravity.START);

        TypedArray atts = context.obtainStyledAttributes(attrs, R.styleable.BatteryMeterView,
                defStyle, 0);
        final int frameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor,
                context.getColor(R.color.meter_background_color));
        mPercentageStyleId = atts.getResourceId(R.styleable.BatteryMeterView_textAppearance, 0);
        /*Bug 1296708 add charge animation of batteryView*/
        //添加电池视图的充电动画 true
        mBatteryAnimation = mContext.getResources().getBoolean(
                R.bool.config_battery_animation);
        
        //将电池等级添加到父布局中
        if (mBatteryAnimation) {
            //mBatteryAnimation为true
            mUnisocDrawable = new BatteryMeterDrawable(context, new Handler(), frameColor, false);
        }else{
            mDrawable = new ThemedBatteryDrawable(context, frameColor);
        }
        /*@}*/

        atts.recycle();

        mSettingObserver = new SettingObserver(new Handler(context.getMainLooper()));
        mShowPercentAvailable = context.getResources().getBoolean(
                com.android.internal.R.bool.config_battery_percentage_setting_available);


        addOnAttachStateChangeListener(
                new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS,
                        Dependency.get(CommandQueue.class)));

        setupLayoutTransition();

        mSlotBattery = context.getString(
                com.android.internal.R.string.status_bar_battery);
        mBatteryIconView = new ImageView(context);
        /*Bug 1296708 add charge animation of batteryView*/
        //添加电池视图的充电动画
        if (mBatteryAnimation) {
            mBatteryIconView.setImageDrawable(mUnisocDrawable);
        }else{
            mBatteryIconView.setImageDrawable(mDrawable);
        }

        final MarginLayoutParams mlp = new MarginLayoutParams(
                getResources().getDimensionPixelSize(R.dimen.status_bar_battery_icon_width),
                getResources().getDimensionPixelSize(R.dimen.status_bar_battery_icon_height));
        mlp.setMargins(0, 0, 0,
                getResources().getDimensionPixelOffset(R.dimen.battery_margin_bottom));
        addView(mBatteryIconView, mlp);

        updateShowPercent();
        mDualToneHandler = new DualToneHandler(context);
        // Init to not dark at all.
        //设置默认的电池布局的主题色,当状态栏主题发生改变时,电池布局会做相应的更换(亮色和暗色切换)
        //在 PhoneStatusBarView 中添加了DarkReceiver监听,最终调用到 BatteryMeterView 的onDarkChanged()方法
        onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);

        //设置 Settings.System.SHOW_BATTERY_PERCENT 监听
        mUserTracker = new CurrentUserTracker(broadcastDispatcher) {
            @Override
            public void onUserSwitched(int newUserId) {
                mUser = newUserId;
                getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
                getContext().getContentResolver().registerContentObserver(
                        Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver,
                        newUserId);
                //当用户点击了显示电量百分比开关,则调用 updateShowPercent()方法在电池等级前添加电量百分比
                updateShowPercent();
            }
        };

        setClipChildren(false);
        setClipToPadding(false);
        Dependency.get(ConfigurationController.class).observe(viewAttachLifecycle(this), this);
    }

②onDarkChanged

    //修改百分比的字体颜色和电池等级的画笔颜色和背景颜色
    @Override
    public void onDarkChanged(Rect area, float darkIntensity, int tint) {
        float intensity = DarkIconDispatcher.isInArea(area, this) ? darkIntensity : 0;
        mNonAdaptedSingleToneColor = mDualToneHandler.getSingleColor(intensity);
        mNonAdaptedForegroundColor = mDualToneHandler.getFillColor(intensity);
        mNonAdaptedBackgroundColor = mDualToneHandler.getBackgroundColor(intensity);

        if (!mUseWallpaperTextColors) {
            //添加电池充电动画
            updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor,
                    mNonAdaptedSingleToneColor);
        }
    }

③updateColors

    //在电池等级前添加电量百分比
    private void updateColors(int foregroundColor, int backgroundColor, int singleToneColor) {
        /*Bug 1296708 add charge animation of batteryView*/
        //添加电池充电动画
        if (mDrawable != null) {
          mDrawable.setColors(foregroundColor, backgroundColor, singleToneColor);
        }
        if (mUnisocDrawable != null) {
          mUnisocDrawable.setColors(foregroundColor, backgroundColor);
        }
        mTextColor = singleToneColor;
        if (mBatteryPercentView != null) {
            mBatteryPercentView.setTextColor(singleToneColor);
        }
    }

2、PhoneStatusBarView

    private DarkReceiver mBattery;

    @Override
    public void onFinishInflate() {
        //
        mBattery = findViewById(R.id.battery);
        mCutoutSpace = findViewById(R.id.cutout_space_view);
        mCenterIconSpace = findViewById(R.id.centered_icon_area);
        updateResources();
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        // Always have Battery meters in the status bar observe the dark/light modes.
        //始终在状态栏的电池仪表观察暗/光模式。
        Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
        if (updateOrientationAndCutout()) {
            updateLayoutForCutout();
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery);
        mDisplayCutout = null;
    }

七、电池状态改变流程

1、BatteryControllerImpl

①init

    @Override
    public void init() {
        //注册广播
        registerReceiver();
        if (!mHasReceivedBattery) {
            // Get initial state. Relying on Sticky behavior until API for getting info.
            Intent intent = mContext.registerReceiver(
                    null,
                    new IntentFilter(Intent.ACTION_BATTERY_CHANGED)
            );
            if (intent != null && !mHasReceivedBattery) {
                onReceive(mContext, intent);
            }
        }
        updatePowerSave();
        updateEstimate();
    }

②registerReceiver

    private void registerReceiver() {
        IntentFilter filter = new IntentFilter();
        //添加广播的方式接收
        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
        
        filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
        /* UNISOC: Bug 1363779 battery icon shows '+' after switching from power saving mode to super power saving @{ */
        filter.addAction(UnisocPowerManagerUtil.ACTION_POWEREX_SAVE_MODE_CHANGED);
        /* @} */
        filter.addAction(ACTION_LEVEL_TEST);
        mBroadcastDispatcher.registerReceiver(this, filter);
    }

③onReceive

    @Override
    public void onReceive(final Context context, Intent intent) {
        final String action = intent.getAction();
        //监听到ACTION_BATTERY_CHANGED
        if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
            if (mTestmode && !intent.getBooleanExtra("testmode", false)) return;
            mHasReceivedBattery = true;
            mLevel = (int)(100f
                    * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
                    / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
            mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;

            final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
                    BatteryManager.BATTERY_STATUS_UNKNOWN);
            mCharged = status == BatteryManager.BATTERY_STATUS_FULL;
            mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;
            //遍历回调监听,将状态参数发送
            fireBatteryLevelChanged();
        }
        ....
    }

④fireBatteryLevelChanged

    protected final ArrayList<BatteryController.BatteryStateChangeCallback>
            mChangeCallbacks = new ArrayList<>(); 

    protected void fireBatteryLevelChanged() {
        synchronized (mChangeCallbacks) {
            final int N = mChangeCallbacks.size();
            //遍历回调监听,将状态参数发送
            for (int i = 0; i < N; i++) {
                mChangeCallbacks.get(i).onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
            }
        }
    }

2、BatteryStateChangeCallback

    interface BatteryStateChangeCallback {

        default void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
        }

        default void onPowerSaveChanged(boolean isPowerSave) {
        }

        default void onReverseChanged(boolean isReverse, int level, String name) {
        }
    }

3、BatteryMeterView

BatteryMeterView实现了 BatteryStateChangeCallback,收到改变监听 onBatteryLevelChanged()

    //实现了 BatteryStateChangeCallback
    public class BatteryMeterView extends LinearLayout implements
        BatteryStateChangeCallback, Tunable, DarkReceiver, ConfigurationListener {
      
    .....
    @Override
    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
        /*Bug 1296708 add charge animation of batteryView*/
        if (mDrawable != null) {
            //是否绘制充电中闪电形状图标
            mDrawable.setCharging(pluggedIn);
            //根据当前 level/100f 计算百分比绘制path
            mDrawable.setBatteryLevel(level);
        }
        mCharging = pluggedIn;
        mLevel = level;
        updatePercentText();
    }
        ....
    }

八、NavigationBar导航栏模块

Ⅰ创建导航栏文件

1、StatusBar

①makeStatusBarView

    // ================================================================================
    // Constructing the view
    // ================================================================================
    protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
        ....
            //创建导航栏
            createNavigationBar(result);
        .....

②createNavigationBar

    private final NavigationBarController mNavigationBarController;

    // TODO(b/117478341): This was left such that CarStatusBar can override this method.
    // Try to remove this.
    protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {
        //
        mNavigationBarController.createNavigationBars(true /* includeDefaultDisplay */, result);

        ...
    }

2、NavigationBarController

①createNavigationBars

    public void createNavigationBars(final boolean includeDefaultDisplay,
            RegisterStatusBarResult result) {
        Display[] displays = mDisplayManager.getDisplays();
        for (Display display : displays) {
            if (includeDefaultDisplay || display.getDisplayId() != DEFAULT_DISPLAY) {
                //
                createNavigationBar(display, result);
            }
        }
    }

②createNavigationBar

   /**
     * Adds a navigation bar on default display or an external display if the display supports
     * system decorations.
     *
     * @param display the display to add navigation bar on.
     */
    @VisibleForTesting
    void createNavigationBar(Display display, RegisterStatusBarResult result) {
        if (display == null) {
            return;
        }

        final int displayId = display.getDisplayId();
        final boolean isOnDefaultDisplay = displayId == DEFAULT_DISPLAY;
        final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();

        try {
            if (!wms.hasNavigationBar(displayId)) {
                return;
            }
        } catch (RemoteException e) {
            // Cannot get wms, just return with warning message.
            Log.w(TAG, "Cannot get WindowManager.");
            return;
        }
        final Context context = isOnDefaultDisplay
                ? mContext
                : mContext.createDisplayContext(display);
        //最终是通过NavigationBarFragment的create方法进行创建
        NavigationBarFragment.create(context, (tag, fragment) -> {
            NavigationBarFragment navBar = (NavigationBarFragment) fragment;

3、NavigationBarFragment

①create

代码做了两件事:

1.创建navigationBarView 并且把navigationBarView添加到windowManager中。

2.创建NavigationBarFragment 替换navigation_bar_window的布局文件,改成navigation_bar

    public static View create(Context context, FragmentListener listener) {
        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
                WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                        | WindowManager.LayoutParams.FLAG_SLIPPERY,
                PixelFormat.TRANSLUCENT);
        lp.token = new Binder();
        lp.setTitle("NavigationBar" + context.getDisplayId());
        lp.accessibilityTitle = context.getString(R.string.nav_bar);
        lp.windowAnimations = 0;
        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;

        View navigationBarView = LayoutInflater.from(context).inflate(
                R.layout.navigation_bar_window, null);

        if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
        if (navigationBarView == null) return null;

        //创建NavigationBarFragment 替换navigation_bar_window的布局文件,改成navigation_bar
        final NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView)
                .create(NavigationBarFragment.class);
        navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
            @Override
            public void onViewAttachedToWindow(View v) {
                final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
                //navigation_bar_frame是navigation_bar_window中NavigationBarFrame的ID
                fragmentHost.getFragmentManager().beginTransaction()
                        .replace(R.id.navigation_bar_frame, fragment, TAG)
                        .commit();
                fragmentHost.addTagListener(TAG, listener);
            }

            @Override
            public void onViewDetachedFromWindow(View v) {
                FragmentHostManager.removeAndDestroy(v);
                navigationBarView.removeOnAttachStateChangeListener(this);
            }
        });
        context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
        return navigationBarView;
    }

③onCreateView

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
            Bundle savedInstanceState) {
        //
        return inflater.inflate(R.layout.navigation_bar, container, false);
    }

4、navigation_bar_window.xml

<com.android.systemui.statusbar.phone.NavigationBarFrame
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:id="@+id/navigation_bar_frame"
    android:theme="@style/Theme.SystemUI"
    android:layout_height="match_parent"
    android:layout_width="match_parent">

</com.android.systemui.statusbar.phone.NavigationBarFrame>

5、navigation_bar.xml

<com.android.systemui.statusbar.phone.NavigationBarView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:background="@drawable/system_bar_background">

    <com.android.systemui.CornerHandleView
        android:id="@+id/assist_hint_left"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="left|bottom"
        android:rotation="270"
        android:visibility="gone"/>
    <com.android.systemui.CornerHandleView
        android:id="@+id/assist_hint_right"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="right|bottom"
        android:rotation="180"
        android:visibility="gone"/>

    <com.android.systemui.statusbar.phone.NavigationBarInflaterView
        android:id="@+id/navigation_inflater"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</com.android.systemui.statusbar.phone.NavigationBarView>

Ⅱ加载布局文件

6、NavigationBarInflaterView

①NavigationBarInflaterView(Context context, AttributeSet attrs)

    public NavigationBarInflaterView(Context context, AttributeSet attrs) {
        super(context, attrs);
        createInflaters();
        mOverviewProxyService = Dependency.get(OverviewProxyService.class);
        mNavBarMode = Dependency.get(NavigationModeController.class).addListener(this);
        /* UNISOC: add for bug 1071183,1134237  @{ */
        mSupportDynamicBar = NavigationBarView.isSupportDynamicNavBar(context, mNavBarMode);
        /* }@ */
    }

②createInflaters

    @VisibleForTesting
    void createInflaters() {
        mLayoutInflater = LayoutInflater.from(mContext);
        Configuration landscape = new Configuration();
        landscape.setTo(mContext.getResources().getConfiguration());
        landscape.orientation = Configuration.ORIENTATION_LANDSCAPE;
        mLandscapeInflater = LayoutInflater.from(mContext.createConfigurationContext(landscape));
    }

③onFinishInflate

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        //添加水平横屏布局
        inflateChildren();
        //清空布局
        clearViews();
        inflateLayout(getDefaultLayout());
    }

④inflateChildren

    private void inflateChildren() {
        removeAllViews();
        //水平布局
        mHorizontal = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout,
                this /* root */, false /* attachToRoot */);
        addView(mHorizontal);
        //垂直布局
        mVertical = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout_vertical,
                this /* root */, false /* attachToRoot */);
        addView(mVertical);
        updatealternativeorder();
    }

⑤clearViews

    private void clearViews() {
        if (mButtonDispatchers != null) {
            for (int i = 0; i < mButtonDispatchers.size(); i++) {
                mButtonDispatchers.valueAt(i).clear();
            }
        }
        clearAllChildren(mHorizontal.findViewById(R.id.nav_buttons));
        clearAllChildren(mVertical.findViewById(R.id.nav_buttons));
    }

    private void clearAllChildren(ViewGroup group) {
        for (int i = 0; i < group.getChildCount(); i++) {
            ((ViewGroup) group.getChildAt(i)).removeAllViews();
        }
    }

⑥inflateLayout

    protected void inflateLayout(String newLayout) {
        mCurrentLayout = newLayout;
        if (newLayout == null) {
            newLayout = getDefaultLayout();
        }
        String[] sets = newLayout.split(GRAVITY_SEPARATOR, 3);
        if (sets.length != 3) {
            Log.d(TAG, "Invalid layout.");
            newLayout = getDefaultLayout();
            sets = newLayout.split(GRAVITY_SEPARATOR, 3);
        }
        String[] start = sets[0].split(BUTTON_SEPARATOR);
        String[] center = sets[1].split(BUTTON_SEPARATOR);
        String[] end = sets[2].split(BUTTON_SEPARATOR);
        // Inflate these in start to end order or accessibility traversal will be messed up.
        inflateButtons(start, mHorizontal.findViewById(R.id.ends_group),
                false /* landscape */, true /* start */);
        inflateButtons(start, mVertical.findViewById(R.id.ends_group),
                true /* landscape */, true /* start */);

        inflateButtons(center, mHorizontal.findViewById(R.id.center_group),
                false /* landscape */, false /* start */);
        inflateButtons(center, mVertical.findViewById(R.id.center_group),
                true /* landscape */, false /* start */);

        addGravitySpacer(mHorizontal.findViewById(R.id.ends_group));
        addGravitySpacer(mVertical.findViewById(R.id.ends_group));

        inflateButtons(end, mHorizontal.findViewById(R.id.ends_group),
                false /* landscape */, false /* start */);
        inflateButtons(end, mVertical.findViewById(R.id.ends_group),
                true /* landscape */, false /* start */);

        updateButtonDispatchersCurrentView();
    }

⑦inflateButton

    private void inflateButtons(String[] buttons, ViewGroup parent, boolean landscape,
                                boolean start) {
        for (int i = 0; i < buttons.length; i++) {
            inflateButton(buttons[i], parent, landscape, start);
        }
    }

    @Nullable
    protected View inflateButton(String buttonSpec, ViewGroup parent, boolean landscape,
                                 boolean start) {
        LayoutInflater inflater = landscape ? mLandscapeInflater : mLayoutInflater;
        View v = createView(buttonSpec, parent, inflater);
        if (v == null) return null;

        v = applySize(v, buttonSpec, landscape, start);
        parent.addView(v);
        addToDispatchers(v);
        View lastView = landscape ? mLastLandscape : mLastPortrait;
        View accessibilityView = v;
        if (v instanceof ReverseRelativeLayout) {
            accessibilityView = ((ReverseRelativeLayout) v).getChildAt(0);
        }
        if (lastView != null) {
            accessibilityView.setAccessibilityTraversalAfter(lastView.getId());
        }
        if (landscape) {
            mLastLandscape = accessibilityView;
        } else {
            mLastPortrait = accessibilityView;
        }
        return v;
    }

⑧getDefaultLayout

    //导航栏显示哪些控件是由getDefaultLayout来决定
    protected String getDefaultLayout() {
        /* UNISOC: Bug 1072090 new feature of dynamic navigationbar @{ */
        if (mSupportDynamicBar) {
            return readLNavigationLayoutSettings();
        }
        /* @} */

        final int defaultResource = QuickStepContract.isGesturalMode(mNavBarMode)
                ? R.string.config_navBarLayoutHandle
                : mOverviewProxyService.shouldShowSwipeUpUI()
                ? R.string.config_navBarLayoutQuickstep
                : R.string.config_navBarLayout;
        return getContext().getString(defaultResource);
    }

⑨createView

    private View createView(String buttonSpec, ViewGroup parent, LayoutInflater inflater) {
        View v = null;
        String button = extractButton(buttonSpec);
        if (LEFT.equals(button)) {
            button = extractButton(NAVSPACE);
        } else if (RIGHT.equals(button)) {
            button = extractButton(MENU_IME_ROTATE);
        }
        if (HOME.equals(button)) {
            v = inflater.inflate(R.layout.home, parent, false);
        } else if (BACK.equals(button)) {
            v = inflater.inflate(R.layout.back, parent, false);
        } else if (RECENT.equals(button)) {
            v = inflater.inflate(R.layout.recent_apps, parent, false);
        } else if (MENU_IME_ROTATE.equals(button)) {
            v = inflater.inflate(R.layout.menu_ime, parent, false);
        } else if (NAVSPACE.equals(button)) {
            v = inflater.inflate(R.layout.nav_key_space, parent, false);
        } else if (CLIPBOARD.equals(button)) {
            v = inflater.inflate(R.layout.clipboard, parent, false);
        } else if (CONTEXTUAL.equals(button)) {
            v = inflater.inflate(R.layout.contextual, parent, false);
        } else if (HOME_HANDLE.equals(button)) {
            v = inflater.inflate(R.layout.home_handle, parent, false);
        } else if (IME_SWITCHER.equals(button)) {
            v = inflater.inflate(R.layout.ime_switcher, parent, false);
        } else if (button.startsWith(KEY)) {
            String uri = extractImage(button);
            int code = extractKeycode(button);
            v = inflater.inflate(R.layout.custom_key, parent, false);
            ((KeyButtonView) v).setCode(code);
            if (uri != null) {
                if (uri.contains(":")) {
                    ((KeyButtonView) v).loadAsync(Icon.createWithContentUri(uri));
                } else if (uri.contains("/")) {
                    int index = uri.indexOf('/');
                    String pkg = uri.substring(0, index);
                    int id = Integer.parseInt(uri.substring(index + 1));
                    ((KeyButtonView) v).loadAsync(Icon.createWithResource(pkg, id));
                }
            }
        }
        /* UNISOC: Bug 1072090 new feature of dynamic navigationbar @{ */
        else if (HIDE.equals(button)) {
            v = inflater.inflate(R.layout.hide, parent, false);
        } else if (PULL.equals(button)) {
            v = inflater.inflate(R.layout.pull, parent, false);
            /*UNISOC: Add for bug 902309 1146896 @{ */
        } else if (SPACE_PLACE.equals(button)) {
            v = inflater.inflate(R.layout.space, parent, false);
            /* }@ */
        } else {
            return null;
        }
        /* @} */
        return v;
    }

7、home.xml、back.xml、recent_apps.xml

<com.android.systemui.statusbar.policy.KeyButtonView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:id="@+id/home"
    android:layout_width="@dimen/navigation_key_width"
    android:layout_height="match_parent"
    android:layout_weight="0"
    systemui:keyCode="3"
    android:scaleType="center"
    android:contentDescription="@string/accessibility_home"
    android:paddingStart="@dimen/navigation_key_padding"
    android:paddingEnd="@dimen/navigation_key_padding"
    />
        

<com.android.systemui.statusbar.policy.KeyButtonView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:id="@+id/back"
    android:layout_width="@dimen/navigation_key_width"
    android:layout_height="match_parent"
    android:layout_weight="0"
    systemui:keyCode="4"
    android:scaleType="center"
    android:contentDescription="@string/accessibility_back"
    android:paddingStart="@dimen/navigation_key_padding"
    android:paddingEnd="@dimen/navigation_key_padding"
    />

        
        
<com.android.systemui.statusbar.policy.KeyButtonView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:id="@+id/recent_apps"
    android:layout_width="@dimen/navigation_key_width"
    android:layout_height="match_parent"
    android:layout_weight="0"
    android:scaleType="center"
    android:contentDescription="@string/accessibility_recent"
    android:paddingStart="@dimen/navigation_key_padding"
    android:paddingEnd="@dimen/navigation_key_padding"
    />

8、config

Android 11 SystemUI 启动流程,Android Framework,android

    <!-- Nav bar button default ordering/layout -->
    <string name="config_navBarLayout" translatable="false">left[.5W],back[1WC];home;recent[1WC],right[.5W]</string>
    <string name="config_navBarLayoutQuickstep" translatable="false">back[1.7WC];home;contextual[1.7WC]</string>
    <string name="config_navBarLayoutHandle" translatable="false">back[40AC];home_handle;ime_switcher[40AC]</string>

第一、导航栏显示哪些控件是由getDefaultLayout来决定。

<string name="config_navBarLayout" translatable="false">left[.5W],back[1WC];home;recent[1WC],right[.5W]</string>

一般情况下我们是home,recent,back这三个键,如果你需要加其他的就在这个配置文件夹。同时在createView添加对应的布局文件。

第二、createView方法创建对应的布局文件,并且添加到导航栏中。

那么我们现在布局文件都添加完成了,但是你会发现在NavigationBarInflaterView没有对资源文件添加的代码已经控件点击触摸事件处理逻辑。那么这两部分代码在哪里呢?

答案是:

1.NavigationBarView 完成资源文件添加。

2.NavigationBarFragment 添加点击事件和触摸事件的处理逻辑。

Ⅲ资源文件添加

1、NavigationBarView

①NavigationBarView(Context context, AttributeSet attrs)

    public NavigationBarView(Context context, AttributeSet attrs) {
        super(context, attrs);

        mIsVertical = false;
        mLongClickableAccessibilityButton = false;
        mNavBarMode = Dependency.get(NavigationModeController.class).addListener(this);
        //UNISOC: Add for bug 1242615
        mOldNavBarMode = mNavBarMode;
        /* UNISCO: Bug 1072090,1116092 new feature of dynamic navigationbar @{*/
        mSupportDynamicBar = isSupportDynamicNavBar(mContext, mNavBarMode);
        mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
        /* }@ */
        /* UNISOC: Modify for bug963304 {@ */
        mStatusBarManager = (StatusBarManager) mContext.getSystemService(android.app.Service.STATUS_BAR_SERVICE);
        /* @} */
        boolean isGesturalMode = isGesturalMode(mNavBarMode);

        mSysUiFlagContainer = Dependency.get(SysUiState.class);
        mPluginManager = Dependency.get(PluginManager.class);
        // Set up the context group of buttons
        mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container);
        final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,
                R.drawable.ic_ime_switcher_default);
        final RotationContextButton rotateSuggestionButton = new RotationContextButton(
                R.id.rotate_suggestion, R.drawable.ic_sysbar_rotate_button);
        final ContextualButton accessibilityButton =
                new ContextualButton(R.id.accessibility_button,
                        R.drawable.ic_sysbar_accessibility_button);
        mContextualButtonGroup.addButton(imeSwitcherButton);
        if (!isGesturalMode) {
            mContextualButtonGroup.addButton(rotateSuggestionButton);
        }
        mContextualButtonGroup.addButton(accessibilityButton);

        mOverviewProxyService = Dependency.get(OverviewProxyService.class);
        mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
        mFloatingRotationButton = new FloatingRotationButton(context);
        mRotationButtonController = new RotationButtonController(context,
                R.style.RotateButtonCCWStart90,
                isGesturalMode ? mFloatingRotationButton : rotateSuggestionButton);

        mConfiguration = new Configuration();
        mTmpLastConfiguration = new Configuration();
        mConfiguration.updateFrom(context.getResources().getConfiguration());

        mScreenPinningNotify = new ScreenPinningNotify(mContext);
        mBarTransitions = new NavigationBarTransitions(this, Dependency.get(CommandQueue.class));

        mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
        mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
        mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle));
        mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
        mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton);
        mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton);
        mButtonDispatchers.put(R.id.rotate_suggestion, rotateSuggestionButton);
        mButtonDispatchers.put(R.id.menu_container, mContextualButtonGroup);
        mDeadZone = new DeadZone(this);

        /* UNISOC: Bug 1072090 new feature of dynamic navigationbar @{ */
        if(mSupportDynamicBar){
            mStatusBar = Dependency.get(StatusBar.class);
            mButtonDispatchers.put(R.id.hide, new ButtonDispatcher(R.id.hide));
            mButtonDispatchers.put(R.id.pull, new ButtonDispatcher(R.id.pull));
        }

        mNavColorSampleMargin = getResources()
                        .getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
        //updateStates更新状态
        mEdgeBackGestureHandler = new EdgeBackGestureHandler(context, mOverviewProxyService,
                mSysUiFlagContainer, mPluginManager, this::updateStates);
        mRegionSamplingHelper = new RegionSamplingHelper(this,
                new RegionSamplingHelper.SamplingCallback() {
                    @Override
                    public void onRegionDarknessChanged(boolean isRegionDark) {
                        getLightTransitionsController().setIconsDark(!isRegionDark ,
                                true /* animate */);
                    }

                    @Override
                    public Rect getSampledRegion(View sampledView) {
                        if (mOrientedHandleSamplingRegion != null) {
                            return mOrientedHandleSamplingRegion;
                        }

                        updateSamplingRect();
                        return mSamplingBounds;
                    }

                    @Override
                    public boolean isSamplingEnabled() {
                        return isGesturalModeOnDefaultDisplay(getContext(), mNavBarMode);
                    }
                });
    }

②updateStates

    public void updateStates() {
        final boolean showSwipeUpUI = mOverviewProxyService.shouldShowSwipeUpUI();

        if (mNavigationInflaterView != null) {
            // Reinflate the navbar if needed, no-op unless the swipe up state changes
            mNavigationInflaterView.onLikelyDefaultLayoutChange();
        }

        updateSlippery();
        //初始化加载资源,主要是图片
        reloadNavIcons();
        updateNavButtonIcons();
        setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
        WindowManagerWrapper.getInstance().setNavBarVirtualKeyHapticFeedbackEnabled(!showSwipeUpUI);
        getHomeButton().setAccessibilityDelegate(
                showSwipeUpUI ? mQuickStepAccessibilityDelegate : null);
    }

③reloadNavIcons

    //初始化加载资源,主要是图片
    private void reloadNavIcons() {
        updateIcons(Configuration.EMPTY);
    }

Ⅳ添加点击事件和触摸事件的处理逻辑。

1、NavigationBarFragment

①onViewCreated

    @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());
     
        添加home,recent触摸事件回调
        prepareNavigationBarView();
        checkNavBarModes();

        IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_USER_SWITCHED);
        //UNISOC: Add for bug 1274603
        filter.addAction(Intent.ACTION_USER_PRESENT);
        filter.addAction(PowerManagerEx.ACTION_POWEREX_SAVE_MODE_CHANGED);
        mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter,
                Handler.getMain(), UserHandle.ALL);
        notifyNavigationBarScreenOn();

        mOverviewProxyService.addCallback(mOverviewProxyListener);
        updateSystemUiStateFlags(-1);
        ......
    }

②prepareNavigationBarView

setOnClickListener,setOnTouchListener,setLongClickable,setOnLongClickListener就是给对应的控件添加控制代码

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

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

        ButtonDispatcher backButton = mNavigationBarView.getBackButton();
        backButton.setLongClickable(true);

        ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
        homeButton.setOnTouchListener(this::onHomeTouch);
        homeButton.setOnLongClickListener(this::onHomeLongClick);

        ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
        accessibilityButton.setOnClickListener(this::onAccessibilityClick);
        accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
        updateAccessibilityServicesState(mAccessibilityManager);

        updateScreenPinningGestures();
    }

③onHomeTouch

    private boolean onHomeTouch(View v, MotionEvent event) {
        if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
            return true;
        }
        // If an incoming call is ringing, HOME is totally disabled.
        // (The user is already on the InCallUI at this point,
        // and his ONLY options are to answer or reject the call.)
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mHomeBlockedThisTouch = false;
                TelecomManager telecomManager =
                        getContext().getSystemService(TelecomManager.class);
                if (telecomManager != null && telecomManager.isRinging()) {
                    if (mStatusBarLazy.get().isKeyguardShowing()) {
                        Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +
                                "No heads up");
                        mHomeBlockedThisTouch = true;
                        return true;
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mStatusBarLazy.get().awakenDreams();
                break;
        }
        return false;
    }

④onHomeLongClick

    @VisibleForTesting
    boolean onHomeLongClick(View v) {
        if (!mNavigationBarView.isRecentsButtonVisible()
                && ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
            return onLongPressBackHome(v);
        }
        if (shouldDisableNavbarGestures()) {
            return false;
        }
        mMetricsLogger.action(MetricsEvent.ACTION_ASSIST_LONG_PRESS);

        /* UNISOC: Bug 1074234, 970184, Super power feature @{ */
        if (UnisocPowerManagerUtil.isSuperPower()) {
            Log.d(TAG, "onHomeLongClick SUPPORT_SUPER_POWER_SAVE ignore!");
            return false;
        }
        /* @} */

        mUiEventLogger.log(NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS);
        Bundle args  = new Bundle();
        args.putInt(
                AssistManager.INVOCATION_TYPE_KEY, AssistManager.INVOCATION_HOME_BUTTON_LONG_PRESS);
        mAssistManager.startAssist(args);
        mStatusBarLazy.get().awakenDreams();

        if (mNavigationBarView != null) {
            mNavigationBarView.abortCurrentGesture();
        }
        return true;
    }

九、Recents模块

packages/apps/SystemUI/src/com/android/systemui/recents/

1、Recents

public class Recents extends SystemUI implements CommandQueue.Callbacks {
    private final RecentsImplementation mImpl;
    private final CommandQueue mCommandQueue;

    public Recents(Context context, RecentsImplementation impl, CommandQueue commandQueue) {
        super(context);
        mImpl = impl;
        mCommandQueue = commandQueue;
    }

    //由前面SystemUI的启动可知,调用的都为子类的start方法
    //在start方法添加了回调和调用了RecentsImplementation的onStart方法,下面跟进RecentsImplementation
    @Override
    public void start() {
        mCommandQueue.addCallback(this);
        mImpl.onStart(mContext);
    }
    ...
}

2、RecentsImplementation

public interface RecentsImplementation {
    //可以看到该接口中方法皆为default修饰的方法,但均未写函数体,具体实现由子类实现,于是跟进OverviewProxyRecentsImpl类
    default void onStart(Context context) {}
    default void onBootCompleted() {}
    default void onAppTransitionFinished() {}
    default void onConfigurationChanged(Configuration newConfig) {}

    default void preloadRecentApps() {}
    default void cancelPreloadRecentApps() {}
    default void showRecentApps(boolean triggeredFromAltTab) {}
    default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {}
    default void toggleRecentApps() {}
    default void growRecents() {}
    default boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds,
            int metricsDockAction) {
        return false;
    }

    default void dump(PrintWriter pw) {}
}

3、OverviewProxyRecentsImpl

/**
 * An implementation of the Recents interface which proxies to the OverviewProxyService.
 */
@Singleton
public class OverviewProxyRecentsImpl implements RecentsImplementation {

    private final static String TAG = "OverviewProxyRecentsImpl";
    @Nullable
    private final Lazy<StatusBar> mStatusBarLazy;
    private final Optional<Divider> mDividerOptional;

    private Context mContext;
    private Handler mHandler;
    private TrustManager mTrustManager;
    private OverviewProxyService mOverviewProxyService;

    @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
    @Inject
    public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy,
            Optional<Divider> dividerOptional) {
        mStatusBarLazy = statusBarLazy.orElse(null);
        mDividerOptional = dividerOptional;
    }
    
    //可见之前调用的onStart()方法具体是调用的该子类的重写方法
    @Override
    public void onStart(Context context) {
        mContext = context;
        mHandler = new Handler();
        mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE);
        mOverviewProxyService = Dependency.get(OverviewProxyService.class);
    }
    
    
     @Override
    public void 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) {
         //可以看到显示最近的app的方法都是通过得到OverviewProxyService的代理,之后对其操作,
         //接着跟进OverviewProxyService类查看overviewProxy的由来
                        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
        }
    }   
}

4、OverviewProxyService

①getProxy

    private IOverviewProxy mOverviewProxy;

    //可以看到getProxy()方法返回的是一个mOverviewProxy:IOverviewProxy对象引用,接下来查看其具体指向哪个对象
    public IOverviewProxy getProxy() {
        return mOverviewProxy;
    }

②ServiceConnection

    //通过mOverviewServiceConnection应该可以发现,应该是bindService中的一个参数。
    private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            if (SysUiState.DEBUG) {
                Log.d(TAG_OPS, "Overview proxy service connected");
            }
            mConnectionBackoffAttempts = 0;
            mHandler.removeCallbacks(mDeferredConnectionCallback);
            try {
                service.linkToDeath(mOverviewServiceDeathRcpt, 0);
            } catch (RemoteException e) {
                // Failed to link to death (process may have died between binding and connecting),
                // just unbind the service for now and retry again
                Log.e(TAG_OPS, "Lost connection to launcher service", e);
                disconnectFromLauncherService();
                retryConnectionWithBackoff();
                return;
            }

            mCurrentBoundedUserId = getCurrentUserId();
            //mOverviewProxy指向了IOverviewProxy的一个远程代理
            mOverviewProxy = IOverviewProxy.Stub.asInterface(service);

            Bundle params = new Bundle();
            params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder());
            params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius);
            params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows);
            try {
                mOverviewProxy.onInitialize(params);
            } catch (RemoteException e) {
                mCurrentBoundedUserId = -1;
                Log.e(TAG_OPS, "Failed to call onInitialize()", e);
            }
            dispatchNavButtonBounds();
            ......
        }

③internalConnectToCurrentUser

    private void internalConnectToCurrentUser() {
        disconnectFromLauncherService();

        // If user has not setup yet or already connected, do not try to connect
        if (!isEnabled()) {
            Log.v(TAG_OPS, "Cannot attempt connection, is enabled " + isEnabled());
            return;
        }
        mHandler.removeCallbacks(mConnectionRunnable);
        //ACTION_QUICKSTEP,这个action就是Launcher中的
        Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP);
        if (mRecentsComponentName != null) {
            launcherServiceIntent.setPackage(mRecentsComponentName.getPackageName());
        }
        try {
            //传入的intent为launcherServiceIntent,其参数为ACTION_QUICKSTEP,查看定义这个action就是Launcher中的
            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);
        } catch (IllegalArgumentException e) {
            Log.e(TAG_OPS, "Unable to bind because of illegal argument error", e);
        }

        if (mBound) {
            // Ensure that connection has been established even if it thinks it is bound
            mHandler.postDelayed(mDeferredConnectionCallback, DEFERRED_CALLBACK_MILLIS);
        } else {
            // Retry after exponential backoff timeout
            retryConnectionWithBackoff();
        }
    }

5、AndroidManifest.xml

代码位于packages/apps/Launcher3/quickstep/AndroidManifest.xml

        <service
            //TouchInteractionService
            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>

6、TouchInteractionService

    private OverviewCommandHelper mOverviewCommandHelper;
   
private final IBinder mMyBinder = new IOverviewProxy.Stub() {

        @BinderThread
        public void onInitialize(Bundle bundle) {
            ISystemUiProxy proxy = ISystemUiProxy.Stub.asInterface(
                    bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
            MAIN_EXECUTOR.execute(() -> {
                SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy);
                TouchInteractionService.this.initInputMonitor();
                preloadOverview(true /* fromInit */);
            });
            sIsInitialized = true;
        }

        @BinderThread
        @Override
        //也就是说,上面OverviewProxyRecentsImpl调用的mOverviewProxyService.getProxy().onOverviewToggle()
        //其实是调用TouchInteractionService中的mMyBinder的实现,mMyBinder就是IOverviewProxy的一个远程代理。
        public void onOverviewToggle() {
            TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
            mOverviewCommandHelper.onOverviewToggle();
        }
        ....
        }

7、OverviewCommandHelper

①onOverviewToggle

    //也就是说,上面OverviewProxyRecentsImpl调用的mOverviewProxyService.getProxy().onOverviewToggle()
    @BinderThread
    public void onOverviewToggle() {
        // If currently screen pinning, do not enter overview
        if (mDeviceState.isScreenPinningActive()) {
            return;
        }

        ActivityManagerWrapper.getInstance()
                .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
        //可以看到这里主要启动了RecentsActivityCommand线程
        MAIN_EXECUTOR.execute(new RecentsActivityCommand<>());
    }

    public RecentsActivityCommand() {
        mActivityInterface = mOverviewComponentObserver.getActivityInterface();
        mCreateTime = SystemClock.elapsedRealtime();
        mAnimationProvider = new AppToOverviewAnimationProvider<>(mActivityInterface,
                RecentsModel.getRunningTaskId(), mDeviceState);

        // Preload the plan
        mRecentsModel.getTasks(null);
    }

②run()

最终实现了一个从点击switch到Launcher的RecentsActivity启动的过程

        
        @Override
        public void run() {
            long elapsedTime = mCreateTime - mLastToggleTime;
            mLastToggleTime = mCreateTime;

            if (handleCommand(elapsedTime)) {
                // Command already handled.
                return;
            }
    
            if (mActivityInterface.switchToRecentsIfVisible(this::onTransitionComplete)) {
                // If successfully switched, then return
                return;
            }

            // 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());
        }

十、VolumeUI模块

这个模块使用MVP架构完成设计的

Android 11 SystemUI 启动流程,Android Framework,android

通过 SystemUI之StatusBar创建 可知,VolumeUI 的入口为 VolumeUI#start()

Ⅰ MVP架构绑定流程

1、VolumeUI

①start()

    @Override
    public void start() {
        boolean enableVolumeUi = mContext.getResources().getBoolean(R.bool.enable_volume_ui);
        boolean enableSafetyWarning =
            mContext.getResources().getBoolean(R.bool.enable_safety_warning);
        mEnabled = enableVolumeUi || enableSafetyWarning;
        if (!mEnabled) return;
        //mVolumeComponent从名字可以看出,它代表 VolumeUI 组件,通过它可以创建整个MVP。
        mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning);
        //register启动VolumeUI的功能
        setDefaultVolumeController();
    }

②VolumeUI(Context context, VolumeDialogComponent volumeDialogComponent)

    //VolumeUI 启动的时候会创建一个 VolumeDialogComponent 对象
    @Inject
    public VolumeUI(Context context, VolumeDialogComponent volumeDialogComponent) {
        super(context);
        mVolumeComponent = volumeDialogComponent;
    }

③setDefaultVolumeController

    private void setDefaultVolumeController() {
        DndTile.setVisible(mContext, true);
        if (D.BUG) Log.d(TAG, "Registering default volume controller");
        //VolumeDialogComponent 对象创建完成后,就会调用它的register()方法启动 VolumeUI 功能。
        //register启动VolumeUI的功能
        //它其实就是关联 Presenter 层和 Model 层。
        mVolumeComponent.register();
    }

④VolumeDialogComponent的register方法

    private final VolumeDialogControllerImpl mController;

    @Override
    public void register() {
        mController.register();
        DndTile.setCombinedIcon(mContext, true);
    }

⑤VolumeDialogControllerImpl的register方法

    public void register() {
        setVolumeController();
        setVolumePolicy(mVolumePolicy);
        showDndTile(mShowDndTile);
        try {
            mMediaSessions.init();
        } catch (SecurityException e) {
            Log.w(TAG, "No access to media sessions", e);
        }
    }

2、VolumeDialogComponent

①VolumeDialogComponent 的构造函数

    //接口,实现类为VolumeDialogImpl
    private VolumeDialog mDialog;

    @Inject
    public VolumeDialogComponent(Context context, KeyguardViewMediator keyguardViewMediator,
            VolumeDialogControllerImpl volumeDialogController) {
        mContext = context;
        mKeyguardViewMediator = keyguardViewMediator;
        mController = volumeDialogController;
        mController.setUserActivityListener(this);
        // Allow plugins to reference the VolumeDialogController.
        Dependency.get(PluginDependencyProvider.class)
                .allowPluginDependency(VolumeDialogController.class);
        Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
                .withPlugin(VolumeDialog.class)
            //VolumeDialogComponent 通过 createDefault() 创建 VolumeDialogImpl 对象,它代表 View 层
                .withDefault(this::createDefault)
                .withCallback(dialog -> {
                    if (mDialog != null) {
                        mDialog.destroy();
                    }
                    mDialog = dialog;
                    //然后通过init() 进行了初始化。
                    mDialog.init(LayoutParams.TYPE_VOLUME_OVERLAY, mVolumeDialogCallback);
                }).build();
        applyConfiguration();
        Dependency.get(TunerService.class).addTunable(this, VOLUME_DOWN_SILENT, VOLUME_UP_SILENT,
                VOLUME_SILENT_DO_NOT_DISTURB);
    }

②createDefault()

    protected VolumeDialog createDefault() {
        VolumeDialogImpl impl = new VolumeDialogImpl(mContext);
        impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
        impl.setAutomute(true);
        impl.setSilentMode(false);
        return impl;
    }

2、VolumeDialogImpl---View层

①init(int windowType, Callback callback)

    public void init(int windowType, Callback callback) {
        initDialog();

        mAccessibility.init();

        //向 VolumeDialogControllerImpl (Presenter层) 注册一个回调
        //也就是 View 层与 Presenter 层建立关联,从而可以通过 Presenter 层控制 View 层。
        mController.addCallback(mControllerCallbackH, mHandler);
        mController.getState();

        Dependency.get(ConfigurationController.class).addCallback(this);
    }

3、VolumeDialogControllerImpl---P层

①register

    
    
    //进行AudioManager的关联,也就是presenter层和model层的关联
    public void register() {
        //进行AudioManager的关联
        setVolumeController();
        
        setVolumePolicy(mVolumePolicy);
        showDndTile(mShowDndTile);
        try {
            mMediaSessions.init();
        } catch (SecurityException e) {
            Log.w(TAG, "No access to media sessions", e);
        }
    }

②setVolumeController()

    private AudioManager mAudio;
    protected final VC mVolumeController = new VC();

    protected void setVolumeController() {
        try {
            mAudio.setVolumeController(mVolumeController);
        } catch (SecurityException e) {
            Log.w(TAG, "Unable to set the volume controller", e);
            return;
        }
    }

Ⅱ 按下 Power 键后,VolumeUI 是如何显示UI的

由于 VolumeDialogControllerImpl 向AudioManager注册了回调,当按下音量键调整了音量后,VolumeDialogControllerImpl 就会收到回调

1、VC(VolumeDialogControllerImpl内部类)

    private final W mWorker;    

    private final class VC extends IVolumeController.Stub {
        private final String TAG = VolumeDialogControllerImpl.TAG + ".VC";
        
        ......
        @Override
        public void volumeChanged(int streamType, int flags) throws RemoteException {
            if (D.BUG) Log.d(TAG, "volumeChanged " + AudioSystem.streamToString(streamType)
                    + " " + Util.audioManagerFlagsToString(flags));
            if (mDestroyed) return;
            //mWorker为继承于Handler的内部final类,根据收到的消息不同处理
            mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();
        }
        ..........
    }

2、W(VolumeDialogControllerImpl内部类)

①handleMessage

    private final class W extends Handler {
        private static final int VOLUME_CHANGED = 1;
        private static final int DISMISS_REQUESTED = 2;
        .....

        W(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                //当消息为VOLUME_CHANGED时,调用onVolumeChangedW方法
                case VOLUME_CHANGED: onVolumeChangedW(msg.arg1, msg.arg2); break;
                case DISMISS_REQUESTED: onDismissRequestedW(msg.arg1); break;
                case GET_STATE: onGetStateW(); break;
                case SET_RINGER_MODE: onSetRingerModeW(msg.arg1, msg.arg2 != 0); break;
                case SET_ZEN_MODE: onSetZenModeW(msg.arg1); break;
                case SET_EXIT_CONDITION: onSetExitConditionW((Condition) msg.obj); break;
                case SET_STREAM_MUTE: onSetStreamMuteW(msg.arg1, msg.arg2 != 0); break;
                case LAYOUT_DIRECTION_CHANGED: mCallbacks.onLayoutDirectionChanged(msg.arg1); break;
                case CONFIGURATION_CHANGED: mCallbacks.onConfigurationChanged(); break;
                case SET_STREAM_VOLUME: onSetStreamVolumeW(msg.arg1, msg.arg2); break;
                case SET_ACTIVE_STREAM: onSetActiveStreamW(msg.arg1); break;
                case NOTIFY_VISIBLE: onNotifyVisibleW(msg.arg1 != 0); break;
                case USER_ACTIVITY: onUserActivityW(); break;
                case SHOW_SAFETY_WARNING: onShowSafetyWarningW(msg.arg1); break;
                case GET_CAPTIONS_COMPONENT_STATE:
                    onGetCaptionsComponentStateW((Boolean) msg.obj); break;
                case ACCESSIBILITY_MODE_CHANGED: onAccessibilityModeChanged((Boolean) msg.obj);
            }
        }
    }

②onVolumeChangedW

    boolean onVolumeChangedW(int stream, int flags) {
        //根据 flags 决定要执行哪个回调,如果要显示UI,就会回调 onShowRequested()
        final boolean showUI = shouldShowUI(flags);
        final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
        final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
        final boolean showSilentHint = (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0;
        boolean changed = false;
        if (showUI) {
            changed |= updateActiveStreamW(stream);
        }
        int lastAudibleStreamVolume = getAudioManagerStreamVolume(stream);
        changed |= updateStreamLevelW(stream, lastAudibleStreamVolume);
        changed |= checkRoutedToBluetoothW(showUI ? AudioManager.STREAM_MUSIC : stream);
        if (changed) {
            mCallbacks.onStateChanged(mState);
        }
        //这个回调当然是由 View 层实现的,也就是在VolumeDialogImpl中调用
        if (showUI) {
            mCallbacks.onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
        }
        if (showVibrateHint) {
            mCallbacks.onShowVibrateHint();
        }
        if (showSilentHint) {
            mCallbacks.onShowSilentHint();
        }
        if (changed && fromKey) {
            Events.writeEvent(Events.EVENT_KEY, stream, lastAudibleStreamVolume);
        }
        return changed;
    }

3、VolumeDialogImpl

①onShowRequested

    
    public void init(int windowType, Callback callback) {
        initDialog();

        mAccessibility.init();
        //初始化的时候添加回调addCallback
        mController.addCallback(mControllerCallbackH, mHandler);
        mController.getState();

        Dependency.get(ConfigurationController.class).addCallback(this);
    }
    //mControllerCallbackH
    private final VolumeDialogController.Callbacks mControllerCallbackH
            = new VolumeDialogController.Callbacks() {
        @Override
        public void onShowRequested(int reason) {
        //
            showH(reason);
        }
        }

②showH

    private void showH(int reason) {
        if (D.BUG) Log.d(TAG, "showH r=" + Events.SHOW_REASONS[reason]);
        mHandler.removeMessages(H.SHOW);
        mHandler.removeMessages(H.DISMISS);
        rescheduleTimeoutH();

        /* UNISOC: Modify for bug1347675,1384445 @{ */
        Configuration config = mContext.getResources().getConfiguration();
        boolean orientationPortrait = config.orientation == ORIENTATION_PORTRAIT;
        if ((mConfigChanged || (mOrientationPortrait != orientationPortrait)) && !mDialog.isShowing()) {
            initDialog(); // resets mShowing to false
            mConfigurableTexts.update();
            mConfigChanged = false;
            mOrientationPortrait = orientationPortrait;
        }
        /* @} */

        initSettingsH();
        mShowing = true;
        mIsAnimatingDismiss = false;
        mDialog.show();
        Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
        mController.notifyVisible(true);
        mController.getCaptionsComponentState(false);
        checkODICaptionsTooltip(false);
    }

ⅢAudioService对音量键处理流程

1、PhoneWindowManager

①dispatchDirectAudioEvent

    // pre-condition: event.getKeyCode() is one of KeyEvent.KEYCODE_VOLUME_UP,
    //                                   KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_MUTE
    private void dispatchDirectAudioEvent(KeyEvent event) {
        // When System Audio Mode is off, volume keys received by AVR can be either consumed by AVR
        // or forwarded to the TV. It's up to Amplifier manufacturer’s implementation.
        HdmiControlManager hdmiControlManager = getHdmiControlManager();
        if (null != hdmiControlManager
                && !hdmiControlManager.getSystemAudioMode()
                && shouldCecAudioDeviceForwardVolumeKeysSystemAudioModeOff()) {
            HdmiAudioSystemClient audioSystemClient = hdmiControlManager.getAudioSystemClient();
            if (audioSystemClient != null) {
                audioSystemClient.sendKeyEvent(
                        event.getKeyCode(), event.getAction() == KeyEvent.ACTION_DOWN);
                return;
            }
        }
        try {
            //这里通过AIDL获取IAudioService的实例
            getAudioService().handleVolumeKey(event, mUseTvRouting,
                    mContext.getOpPackageName(), TAG);
        } catch (Exception e) {
            Log.e(TAG, "Error dispatching volume key in handleVolumeKey for event:"
                    + event, e);
        }
    }

②getAudioService()

    
    import android.media.IAudioService;

    static IAudioService getAudioService() {
        IAudioService audioService = IAudioService.Stub.asInterface(
                ServiceManager.checkService(Context.AUDIO_SERVICE));
        if (audioService == null) {
            Log.w(TAG, "Unable to find IAudioService interface.");
        }
        return audioService;
    }

这里是直接执行了音频键的操作,通过Binder获取到了AudioService的实例,去调用了handleVolumeKey方法,参数含义如下:

按键类型

Audio Service操作类型

含义

KEYCODE_VOLUME_UP

AudioManager.ADJUST_RAISE

音量加

KEYCODE_VOLUME_DOWN

AudioManager.ADJUST_LOWER

音量减

KEYCODE_VOLUME_MUTE

AudioManager.ADJUST_TOGGLE_MUTE

改变静音状态文章来源地址https://www.toymoban.com/news/detail-545738.html

2、AudioService

①handleVolumeKey

//AudioService继承了IAudioService
public class AudioService extends IAudioService.Stub
        implements AccessibilityManager.TouchExplorationStateChangeListener,
            AccessibilityManager.AccessibilityServicesStateChangeListener {
            
    // pre-condition: event.getKeyCode() is one of KeyEvent.KEYCODE_VOLUME_UP,
    //                                   KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_MUTE
    public void handleVolumeKey(@NonNull KeyEvent event, boolean isOnTv,
            @NonNull String callingPackage, @NonNull String caller) {
        int keyEventMode = VOL_ADJUST_NORMAL;
        if (isOnTv) {
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                keyEventMode = VOL_ADJUST_START;
            } else { // may catch more than ACTION_UP, but will end vol adjustement
                // the vol key is either released (ACTION_UP), or multiple keys are pressed
                // (ACTION_MULTIPLE) and we don't know what to do for volume control on CEC, end
                // the repeated volume adjustement
                keyEventMode = VOL_ADJUST_END;
            }
        } else if (event.getAction() != KeyEvent.ACTION_DOWN) {
            return;
        }

        int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND
                | AudioManager.FLAG_FROM_KEY;

        switch (event.getKeyCode()) {
            case KeyEvent.KEYCODE_VOLUME_UP:
                //在按键的处理过程中,并没有将相应的code传递给AudioService,
                //而是使用了相关的定义,将KEYCODE_VOLUME_UP等操作转化为了ADJUST_RAISE等。
                //而flag存储了一些对音量的要求或者信息吧,这个也很重要。
                    adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE,
                            AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller,
                            Binder.getCallingUid(), true, keyEventMode);
                break;
            case KeyEvent.KEYCODE_VOLUME_DOWN:
                    adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER,
                            AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller,
                            Binder.getCallingUid(), true, keyEventMode);
                break;
            case KeyEvent.KEYCODE_VOLUME_MUTE:
                if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
                    adjustSuggestedStreamVolume(AudioManager.ADJUST_TOGGLE_MUTE,
                            AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller,
                            Binder.getCallingUid(), true, VOL_ADJUST_NORMAL);
                }
                break;
            default:
                Log.e(TAG, "Invalid key code " + event.getKeyCode() + " sent by " + callingPackage);
                return; // not needed but added if code gets added below this switch statement
        }
    }
      
}

②adjustSuggestedStreamVolume

    private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
            String callingPackage, String caller, int uid, boolean hasModifyAudioSettings,
            int keyEventMode) {
        if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream=" + suggestedStreamType
                + ", flags=" + flags + ", caller=" + caller
                + ", volControlStream=" + mVolumeControlStream
                + ", userSelect=" + mUserSelectedVolumeControlStream);
        if (direction != AudioManager.ADJUST_SAME) {
            sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_SUGG_VOL, suggestedStreamType,
                    direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage)
                    .append("/").append(caller).append(" uid:").append(uid).toString()));
        }

        boolean hasExternalVolumeController = notifyExternalVolumeController(direction);

        new MediaMetrics.Item(mMetricsId + "adjustSuggestedStreamVolume")
                .setUid(Binder.getCallingUid())
                .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackage)
                .set(MediaMetrics.Property.CLIENT_NAME, caller)
                .set(MediaMetrics.Property.DIRECTION, direction > 0
                        ? MediaMetrics.Value.UP : MediaMetrics.Value.DOWN)
                .set(MediaMetrics.Property.EXTERNAL, hasExternalVolumeController
                        ? MediaMetrics.Value.YES : MediaMetrics.Value.NO)
                .set(MediaMetrics.Property.FLAGS, flags)
                .record();

        if (hasExternalVolumeController) {
            return;
        }

        final int streamType;
        synchronized (mForceControlStreamLock) {
            // Request lock in case mVolumeControlStream is changed by other thread.
            if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1
                streamType = mVolumeControlStream;
            } else {
                // 这里获取到,可能是活动状态的音频流,但是不确定,还有待进一步确认
                final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);
                final boolean activeForReal;
                if (maybeActiveStreamType == AudioSystem.STREAM_RING
                        || maybeActiveStreamType == AudioSystem.STREAM_NOTIFICATION) {
                    activeForReal = wasStreamActiveRecently(maybeActiveStreamType, 0);
                } else {
                    activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);
                }
                if (activeForReal || mVolumeControlStream == -1) {
                    streamType = maybeActiveStreamType;
                } else {
                // activeForReal为false并且mVolumeControlStream不为-1
                // 表示用户点击了音量进度条,这时候要操作修改的流类型为mVolumeControlStream对应的流类型
                    streamType = mVolumeControlStream;
                }
            }
        }

        final boolean isMute = isMuteAdjust(direction);
        // 确保我们获取到的流类型是有效的
        ensureValidStreamType(streamType);
        // 将我们获取到的流,进行流映射,拿到最终需要操作的流类型
        final int resolvedStream = mStreamVolumeAlias[streamType];

        // Play sounds on STREAM_RING only.
        if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
                resolvedStream != AudioSystem.STREAM_RING) {
            flags &= ~AudioManager.FLAG_PLAY_SOUND;
        }

        // For notifications/ring, show the ui before making any adjustments
        // Don't suppress mute/unmute requests
        // Don't suppress adjustments for single volume device
        // 通知和响铃,调整音量之前先显示UI。
        if (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute)
                && !mIsSingleVolume) {
            direction = 0;
            flags &= ~AudioManager.FLAG_PLAY_SOUND;
            flags &= ~AudioManager.FLAG_VIBRATE;
            if (DEBUG_VOL) Log.d(TAG, "Volume controller suppressed adjustment");
        }
        // 这里设置音量
        adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid,
                hasModifyAudioSettings, keyEventMode);
    }

suppressAdjustment:字面意思为抑制调整,为什么抑制调整呢,说白了,当我们没有显示音量的UI进度条的时候,不管我们是加音量还是减音量(注意:静音和解静音除外),这个时候都是先显示音量条,而不去改变音量的大小。所以当这个方法返回true的时候, direction = 0,这里direction为0就表示我们的操作为ADJUST_SAME,大家可以在AudioManager里面查看ADJUST_SAME的注释就知道这个操作表示只弹出UI但是不调整音量大小。

③adjustStreamVolume

    protected void adjustStreamVolume(int streamType, int direction, int flags,
            String callingPackage, String caller, int uid, boolean hasModifyAudioSettings,
            int keyEventMode) {
        //mUseFixedVolume表示使用固定音量,我们无法修改音量
        if (mUseFixedVolume) {
            return;
        }
        if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream=" + streamType + ", dir=" + direction
                + ", flags=" + flags + ", caller=" + caller);

        ensureValidDirection(direction);
        ensureValidStreamType(streamType);

        boolean isMuteAdjust = isMuteAdjust(direction);
        
        if (isMuteAdjust && !isStreamAffectedByMute(streamType)) {
            return;
        }

        // If adjust is mute and the stream is STREAM_VOICE_CALL or STREAM_BLUETOOTH_SCO, make sure
        // that the calling app have the MODIFY_PHONE_STATE permission.
        if (isMuteAdjust &&
            (streamType == AudioSystem.STREAM_VOICE_CALL ||
                streamType == AudioSystem.STREAM_BLUETOOTH_SCO) &&
            mContext.checkCallingOrSelfPermission(
                android.Manifest.permission.MODIFY_PHONE_STATE)
                    != PackageManager.PERMISSION_GRANTED) {
            Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: adjustStreamVolume from pid="
                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
            return;
        }

        // If the stream is STREAM_ASSISTANT,
        // make sure that the calling app have the MODIFY_AUDIO_ROUTING permission.
        if (streamType == AudioSystem.STREAM_ASSISTANT &&
            mContext.checkCallingOrSelfPermission(
                android.Manifest.permission.MODIFY_AUDIO_ROUTING)
                    != PackageManager.PERMISSION_GRANTED) {
            Log.w(TAG, "MODIFY_AUDIO_ROUTING Permission Denial: adjustStreamVolume from pid="
                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
            return;
        }

        // use stream type alias here so that streams with same alias have the same behavior,
        // including with regard to silent mode control (e.g the use of STREAM_RING below and in
        // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
        //进行音频流的映射,拿到映射后的音频流
        int streamTypeAlias = mStreamVolumeAlias[streamType];
        
        //mStreamStates是一个存储VolumeStreamState类型的数组,保存着每个音频流的状态。
        //VolumeStreamState是AudioService的一个内部类,里面保存单个音频流的所有信息,比如流类型,音量大小,mute状态等。
        //并且相同的流类型,在不同的设备,大小也是不一样的(比如耳机和扬声器,媒体音量大小是不一样的)
        //这也是在VolumeStreamState里面去维护的。

        VolumeStreamState streamState = mStreamStates[streamTypeAlias];

        final int device = getDeviceForStream(streamTypeAlias);

        int aliasIndex = streamState.getIndex(device);
        boolean adjustVolume = true;
        int step;

        // skip a2dp absolute volume control request when the device
        // is not an a2dp device
        if (!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
                && (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) {
            return;
        }

        // If we are being called by the system (e.g. hardware keys) check for current user
        // so we handle user restrictions correctly.
        if (uid == android.os.Process.SYSTEM_UID) {
            uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
        }
        if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
                != AppOpsManager.MODE_ALLOWED) {
            return;
        }

        // reset any pending volume command
        // 清除掉任何待处理的音量命令
        synchronized (mSafeMediaVolumeStateLock) {
            mPendingVolumeCommand = null;
        }
       // 表示不是固定音量 
        flags &= ~AudioManager.FLAG_FIXED_VOLUME;
        // 如果是多媒体音量,并且是使用固定音量的设备
        if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {
            // 加上表示固定音量的flag
            flags |= AudioManager.FLAG_FIXED_VOLUME;

            // Always toggle between max safe volume and 0 for fixed volume devices where safe
            // volume is enforced, and max and 0 for the others.
            // This is simulated by stepping by the full allowed volume range
            if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
                    mSafeMediaVolumeDevices.contains(device)) {
                step = safeMediaVolumeIndex(device);
            } else {
                step = streamState.getMaxIndex();
            }
            if (aliasIndex != 0) {
                aliasIndex = step;
            }
        } else {
            // convert one UI step (+/-1) into a number of internal units on the stream alias
            // 如果不是多媒体音量,或者是多媒体音量但是不是固定音量的设备时
            // 将音量值的步进量从源流类型变换到目标流类型下,由于不同的流类型的音量调节范围不同,所以这个转换是必需的
            step = rescaleStep(10, streamType, streamTypeAlias);
        }
        
        // // 情景模式的处理
        // If either the client forces allowing ringer modes for this adjustment,
        // or the stream type is one that is affected by ringer modes
        if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
                (streamTypeAlias == getUiSoundsStreamType())) {
            int ringerMode = getRingerModeInternal();
            // do not vibrate if already in vibrate mode
            // 如果已经是震动模式,则不进行震动
            if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
                flags &= ~AudioManager.FLAG_VIBRATE;
            }
            // Check if the ringer mode handles this adjustment. If it does we don't
            // need to adjust the volume further.
             // 根据我们的操作来检查是否需要切换情景模式
            final int result = checkForRingerModeChange(aliasIndex, direction, step,
                    streamState.mIsMuted, callingPackage, flags);
            adjustVolume = (result & FLAG_ADJUST_VOLUME) != 0;
            // If suppressing a volume adjustment in silent mode, display the UI hint
            if ((result & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
                flags |= AudioManager.FLAG_SHOW_SILENT_HINT;
            }
            // If suppressing a volume down adjustment in vibrate mode, display the UI hint
            if ((result & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0) {
                flags |= AudioManager.FLAG_SHOW_VIBRATE_HINT;
            }
        }

        // If the ringer mode or zen is muting the stream, do not change stream unless
        // it'll cause us to exit dnd
        // 勿扰模式
        if (!volumeAdjustmentAllowedByDnd(streamTypeAlias, flags)) {
            adjustVolume = false;
        }
        // 获取旧的音量大小
        int oldIndex = mStreamStates[streamType].getIndex(device);

        if (adjustVolume
                && (direction != AudioManager.ADJUST_SAME) && (keyEventMode != VOL_ADJUST_END)) {
            mAudioHandler.removeMessages(MSG_UNMUTE_STREAM);
            // 先处理静音调整
            if (isMuteAdjust) {
                boolean state;
                if (direction == AudioManager.ADJUST_TOGGLE_MUTE) {
                    state = !streamState.mIsMuted;
                } else {
                    state = direction == AudioManager.ADJUST_MUTE;
                }
                if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
                    setSystemAudioMute(state);
                }
                for (int stream = 0; stream < mStreamStates.length; stream++) {
                    if (streamTypeAlias == mStreamVolumeAlias[stream]) {
                        if (!(readCameraSoundForced()
                                    && (mStreamStates[stream].getStreamType()
                                        == AudioSystem.STREAM_SYSTEM_ENFORCED))) {
                            // 这里获取当前流对应的VolumeStreamState实例,然后去调用mute方法
                            // 最终也会到AudioSystem去调用native方法
                            mStreamStates[stream].mute(state);
                        }
                    }
                }
            } else if ((direction == AudioManager.ADJUST_RAISE) &&
                    !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
                // 安全音量提示,音量增加的时候才会去检测
                Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex);
                mVolumeController.postDisplaySafeVolumeWarning(flags);
            } else if (!isFullVolumeDevice(device)
                    && (streamState.adjustIndex(direction * step, device, caller,
                            hasModifyAudioSettings)
                            || streamState.mIsMuted)) {
                // Post message to set system volume (it in turn will post a
                // message to persist).
                if (streamState.mIsMuted) {
                    // Unmute the stream if it was previously muted
                    if (direction == AudioManager.ADJUST_RAISE) {
                        // unmute immediately for volume up
                        streamState.mute(false);
                    } else if (direction == AudioManager.ADJUST_LOWER) {
                        if (mIsSingleVolume) {
                            sendMsg(mAudioHandler, MSG_UNMUTE_STREAM, SENDMSG_QUEUE,
                                    streamTypeAlias, flags, null, UNMUTE_STREAM_DELAY);
                        }
                    }
                }
                // 设置音量到底层
                sendMsg(mAudioHandler,
                        MSG_SET_DEVICE_VOLUME,
                        SENDMSG_QUEUE,
                        device,
                        0,
                        streamState,
                        0);
            }

            int newIndex = mStreamStates[streamType].getIndex(device);

            // Check if volume update should be send to AVRCP
            if (streamTypeAlias == AudioSystem.STREAM_MUSIC
                    && AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
                    && (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
                if (DEBUG_VOL) {
                    Log.d(TAG, "adjustSreamVolume: postSetAvrcpAbsoluteVolumeIndex index="
                            + newIndex + "stream=" + streamType);
                }
                mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(newIndex / 10);
            }

            // Check if volume update should be send to Hearing Aid
            if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {
                // only modify the hearing aid attenuation when the stream to modify matches
                // the one expected by the hearing aid
                if (streamType == getHearingAidStreamType()) {
                    if (DEBUG_VOL) {
                        Log.d(TAG, "adjustSreamVolume postSetHearingAidVolumeIndex index="
                                + newIndex + " stream=" + streamType);
                    }
                    mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType);
                }
            }

            // Check if volume update should be sent to Hdmi system audio.
            if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
                setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags);
            }
        }

        final int newIndex = mStreamStates[streamType].getIndex(device);

        if (adjustVolume) {
            synchronized (mHdmiClientLock) {
                if (mHdmiManager != null) {
                    // mHdmiCecSink true => mHdmiPlaybackClient != null
                    if (mHdmiCecSink
                            && mHdmiCecVolumeControlEnabled
                            && streamTypeAlias == AudioSystem.STREAM_MUSIC
                            // vol change on a full volume device
                            && isFullVolumeDevice(device)) {
                        int keyCode = KeyEvent.KEYCODE_UNKNOWN;
                        switch (direction) {
                            case AudioManager.ADJUST_RAISE:
                                keyCode = KeyEvent.KEYCODE_VOLUME_UP;
                                break;
                            case AudioManager.ADJUST_LOWER:
                                keyCode = KeyEvent.KEYCODE_VOLUME_DOWN;
                                break;
                            case AudioManager.ADJUST_TOGGLE_MUTE:
                                keyCode = KeyEvent.KEYCODE_VOLUME_MUTE;
                                break;
                            default:
                                break;
                        }
                        if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
                            final long ident = Binder.clearCallingIdentity();
                            try {
                                final long time = java.lang.System.currentTimeMillis();
                                switch (keyEventMode) {
                                    case VOL_ADJUST_NORMAL:
                                        mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, true);
                                        mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, false);
                                        break;
                                    case VOL_ADJUST_START:
                                        mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, true);
                                        break;
                                    case VOL_ADJUST_END:
                                        mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, false);
                                        break;
                                    default:
                                        Log.e(TAG, "Invalid keyEventMode " + keyEventMode);
                                }
                            } finally {
                                Binder.restoreCallingIdentity(ident);
                            }
                        }
                    }

                    if (streamTypeAlias == AudioSystem.STREAM_MUSIC
                            && (oldIndex != newIndex || isMuteAdjust)) {
                        maybeSendSystemAudioStatusCommand(isMuteAdjust);
                    }
                }
            }
        }
        // 通知外界音量发生变化
        sendVolumeUpdate(streamType, oldIndex, newIndex, flags, device);
    }

④sendVolumeUpdate

    // UI update and Broadcast Intent
    protected void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags, int device)
    {
        streamType = mStreamVolumeAlias[streamType];

        if (streamType == AudioSystem.STREAM_MUSIC) {
            flags = updateFlagsForTvPlatform(flags);
            if (isFullVolumeDevice(device)) {
                flags &= ~AudioManager.FLAG_SHOW_UI;
            }
        }
        mVolumeController.postVolumeChanged(streamType, flags);
    }

3、AudioManager

    /**
     * Increase the ringer volume.
     *
     * @see #adjustVolume(int, int)
     * @see #adjustStreamVolume(int, int, int)
     */
    public static final int ADJUST_RAISE = 1;

    /**
     * Decrease the ringer volume.
     *
     * @see #adjustVolume(int, int)
     * @see #adjustStreamVolume(int, int, int)
     */
    public static final int ADJUST_LOWER = -1;

    /**
     * Maintain the previous ringer volume. This may be useful when needing to
     * show the volume toast without actually modifying the volume.
     *
     * @see #adjustVolume(int, int)
     * @see #adjustStreamVolume(int, int, int)
     */
    public static final int ADJUST_SAME = 0;

    /**
     * Mute the volume. Has no effect if the stream is already muted.
     *
     * @see #adjustVolume(int, int)
     * @see #adjustStreamVolume(int, int, int)
     */
    public static final int ADJUST_MUTE = -100;

    /**
     * Unmute the volume. Has no effect if the stream is not muted.
     *
     * @see #adjustVolume(int, int)
     * @see #adjustStreamVolume(int, int, int)
     */
    public static final int ADJUST_UNMUTE = 100;

    /**
     * Toggle the mute state. If muted the stream will be unmuted. If not muted
     * the stream will be muted.
     *
     * @see #adjustVolume(int, int)
     * @see #adjustStreamVolume(int, int, int)
     */
    public static final int ADJUST_TOGGLE_MUTE = 101;

到了这里,关于Android 11 SystemUI 启动流程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • android framework之Applicataion启动流程分析

    Application启动流程框架分析 启动方式一:通过Launcher启动app 启动方式二:在某一个app里启动第二个app的Activity. 以上两种方式均可触发app进程的启动。但无论哪种方式,最终通过通过调用AMS的startActivity()来启动application的。    根据上图分析, 要启动一个Application,需要涉及五

    2024年02月11日
    浏览(46)
  • 【Android Framework系列】5章 AMS启动流程

    AMS(Activity Manager Service) 是 Android 中最核心的服务,管理着 四大组件的启动 、 切换 、 调度 及 应用进程的管理和调度 等工作。AndroidQ将Activity移到了 ActivityTaskManagerService 中,但也和AMS相关联。 AMS 通过使用一些系统资源和数据结构(如进程、任务栈、记录四大组件生命周期

    2024年02月15日
    浏览(54)
  • [Android14] SystemUI的启动

            SystemUI是Android系统级应用,负责反馈系统及应用状态并与用户保持大量的交互。业务主要涉及的组成部分包括状态栏(Status Bar),通知栏(Notification Panel),锁屏(Keyguard),控制中心(Quick Setting),音量调节(VolumeUI), 近期任务(Recents)等等。 图例如下所示: package name:    

    2024年04月27日
    浏览(44)
  • Android 11.0 SystemUI下拉状态栏禁止QuickQSPanel展开

       在11.0系统rom产品定制化开发中,对于SystemUI定制开发功能也有不少的,增加快捷功能模块,隐藏状态栏图标项目开发需要要求定制QSPanel相关UI和开发功能, 要求首次下拉后展示快捷功能模块以后就是显示QuickQSPanel而不展开QSPanel,接下来要从下滑手势下拉出状态栏分析功能

    2024年02月08日
    浏览(73)
  • Android 11.0 SystemUI 音量条UI定制的功能(一)

    目录 1.概述 2.SystemUI 音量条UI定制的功能(一)的核心类 3.SystemUI 音量条UI定制的

    2024年02月11日
    浏览(48)
  • Android 11.0 systemui锁屏页面时钟显示样式的定制功能实现

      在11.0的系统ROM定制化开发中,在进行systemui的相关开发中,当开机完成后在锁屏页面就会显示时间日期的功能,由于 开发产品的需求要求时间显示周几上午下午接下来就需要对锁屏显示时间日期的相关布局进行分析,然后实现相关功能 效果图如图: SystemUI作为整个系统的基

    2024年02月04日
    浏览(92)
  • Android 11.0 SystemUI禁用长按recent键的分屏功能

    在11.0的系统rom产品定制化中,系统对于多窗口模式默认会有分屏功能的,但是在某些产品中,需要禁用分屏模式,所以需要在导航栏中 禁用长按recent的分屏模式功能,接下来分析下相关分屏模式的实现 在Android应用程序中,导航栏(NavigationBarView)是一个重要的UI组件,默认位于屏

    2024年02月20日
    浏览(92)
  • Android 12 源码分析 —— 应用层 二(SystemUI大体组织和启动过程)

    在前一篇文章中,我们介绍了SystemUI怎么使用IDE进行编辑和调试。这是分析SystemUI的最基础,希望读者能尽量掌握。 本篇文章,将会介绍SystemUI的大概组织架构,以及它的启动过程。本篇文章读完,将会知道: SystemUI为什么选择使用Dagger2 SystemUI怎么新建一个模块 SystemUI的启动

    2024年02月06日
    浏览(81)
  • Android 11.0 SystemUI 去掉状态栏wifi流量上下行图标功能实现

      在11.0系统定制rom开发中,在关于systemui的定制功能总,在SystemUI 状态栏上显示时钟,电池电量 wifi图标,在显示wifi图标时,网络实时更新时,但是会时不时显示上下行图标 显得很不美观,客户需求要求不显示上下行图标,所以需要去掉上下行图标功能,接下来实现相关功能开

    2024年02月04日
    浏览(59)
  • Android Framework学习之Activity启动原理

    Android 13.0 Activity启动原理逻辑流程图如下:

    2024年02月05日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包