当前位置:   article > 正文

Android 11 SystemUI 启动流程

systemui

SystemUI 有哪内容

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

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

下面是PhoneStatusBarView的view 树形图:

PanelHolder

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

架构关系

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

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

②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

    <!-- 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架构完成设计的

通过 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

改变静音状态

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;
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/120560
推荐阅读
相关标签
  

闽ICP备14008679号