iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >Android 11 SystemUI 启动流程
  • 660
分享到

Android 11 SystemUI 启动流程

android 2023-09-11 07:09:16 660人浏览 独家记忆
摘要

SystemUI 有哪内容 从表面上看, 我们看到的状态栏、通知栏、下拉菜单、导航栏、锁屏、最近任务、低电提示等系统页面都是 SystemUI 的。SystemUI,在源码目录中位于: framework/base/packages 目录下

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 是一个抽象类

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

一、SystemUI的启动流程

1、SystemServer

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

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

①main

        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( "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

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

        com.android.systemui.statusbar.phone.StatusBar

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

            //        //                                                                                                //                                                            //居中的图标区域                                    //                            //    

4、NotificationIconAreaController

①getNotificationInnerAreaView

        public View getNotificationInnerAreaView() {        return mNotificationIconArea;    }

②initializeNotificationAreaViews

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

                                                

2、system_icons.xml

    //StatusIconContainer继承AlphaoptimizedLinearLayout        

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));        }        // TINNO BEGIN, add for KGDAANWIKFRA-135        if (Utils.TINNO_NOTCH_SCREEN) {            mStatusBarDarkView = mStatusBar.findViewById(R.id.status_bar_dark_view);            toggleStatusBarDarkView();            mLastOrientation = getContext().getResources().getConfiguration().orientation;        }        // TINNO END        //DarkIconManager初始化        mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons),                Dependency.get(CommandQueue.class));                mDarkIconManager.setShouldLog(true);        Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);        mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);        mClockView = mStatusBar.findViewById(R.id.clock);        showSystemIconArea(false);        showClock(false);        initEmergencyCryptkeeperText();        initOperatorName();    }

4、StatusBarIconController

①DarkIconManager

        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

//继承StatusBarIconListpublic 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 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/status_bar_rotate        @string/status_bar_headset        @string/status_bar_data_saver        @string/status_bar_managed_profile        @string/status_bar_ime        @string/status_bar_sync_failing        @string/status_bar_sync_active        @string/status_bar_cast        @string/status_bar_hotspot        @string/status_bar_location        @string/status_bar_bluetooth        @string/status_bar_nfc        @string/status_bar_tty        @string/status_bar_speakerphone        @string/status_bar_zen        @string/status_bar_mute        @string/status_bar_volume        @string/status_bar_vpn        @string/status_bar_ethernet        @string/status_bar_wifi        @string/status_bar_mobile        @string/status_bar_airplane        @string/status_bar_cdma_eri        @string/status_bar_data_connection        @string/status_bar_phone_evdo_signal        @string/status_bar_phone_signal        @string/status_bar_battery        @string/status_bar_alarm_clock        @string/status_bar_secure        @string/status_bar_clock         rotate    headset    data_saver    managed_profile    ime    sync_failing    sync_active    cast    hotspot    location    bluetooth    nfc    tty    speakerphone    zen    mute    volume    wifi    cdma_eri    data_connection    phone_evdo_signal    phone_signal    battery    alarm_clock    secure    clock    mobile    vpn    ethernet    airplane

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

四、状态显示流程

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

1、PhoneStatusBarPolicy

初始化注册了大量的监听

①init

//  初始化headset的slotmSlotHeadset = resources.getString(com.android.internal.R.string.status_bar_headset);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 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 {                        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);            if (!audioManager.isWiredHeadsetOn()) {                //setIconVisibility则根据connected状态设置icon的可见性                mIconController.setIconVisibility(mSlotHeadset, false);            }                    }    }

2、StatusBarIconControllerImpl

①setIcon

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

        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 );        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

        public StatusBarWindowView getStatusBarWindowView() {        if (mStatusBarWindowView != null) {            return mStatusBarWindowView;        }        //由其可知加载的布局来自于super_status_bar        mStatusBarWindowView =                (StatusBarWindowView) mInjectionInflationController.injectable(                LayoutInflater.from(mContext)).inflate(R.layout.super_status_bar,                 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(状态栏通知显示区域),完成图标的显示

                        

六、电池图标刷新流程

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);                //添加电池视图的充电动画 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);                //添加电池视图的充电动画        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) {                //添加电池充电动画        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);                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            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) {                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 , 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

       @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

5、navigation_bar.xml

            

Ⅱ加载布局文件

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);                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 , false );        addView(mHorizontal);        //垂直布局        mVertical = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout_vertical,                this , false );        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 , true );        inflateButtons(start, mVertical.findViewById(R.id.ends_group),                true , true );        inflateButtons(center, mHorizontal.findViewById(R.id.center_group),                false , false );        inflateButtons(center, mVertical.findViewById(R.id.center_group),                true , false );        addGravitySpacer(mHorizontal.findViewById(R.id.ends_group));        addGravitySpacer(mVertical.findViewById(R.id.ends_group));        inflateButtons(end, mHorizontal.findViewById(R.id.ends_group),                false , false );        inflateButtons(end, mVertical.findViewById(R.id.ends_group),                true , false );        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() {                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));                }            }        }                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);                    } 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

                        

8、config

        left[.5W],back[1WC];home;recent[1WC],right[.5W]    back[1.7WC];home;contextual[1.7WC]    back[40AC];home_handle;ime_switcher[40AC]

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

left[.5W],back[1WC];home;recent[1WC],right[.5W]

一般情况下我们是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;                mSupportDynamicBar = isSupportDynamicNavBar(mContext, mNavBarMode);        mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);                        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);                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 );                    }                    @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);                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

@Singletonpublic class OverviewProxyRecentsImpl implements RecentsImplementation {    private final static String TAG = "OverviewProxyRecentsImpl";    @Nullable    private final Lazy mStatusBarLazy;    private final Optional mDividerOptional;    private Context mContext;    private Handler mHandler;    private TrustManager mTrustManager;    private OverviewProxyService mOverviewProxyService;    @SuppressWarnings("OptionalUsedAsFieldOrParameterType")    @Inject    public OverviewProxyRecentsImpl(Optional> statusBarLazy,            Optional 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 , false ,                    true );            } 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

                                                        

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 );            });            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();                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继承了IAudioServicepublic 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, flags, 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

        public static final int ADJUST_RAISE = 1;        public static final int ADJUST_LOWER = -1;        public static final int ADJUST_SAME = 0;        public static final int ADJUST_MUTE = -100;        public static final int ADJUST_UNMUTE = 100;        public static final int ADJUST_TOGGLE_MUTE = 101;

来源地址:https://blog.csdn.net/weixin_47465999/article/details/131596152

--结束END--

本文标题: Android 11 SystemUI 启动流程

本文链接: https://www.lsjlt.com/news/402805.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android 11 SystemUI 启动流程
    SystemUI 有哪内容 从表面上看, 我们看到的状态栏、通知栏、下拉菜单、导航栏、锁屏、最近任务、低电提示等系统页面都是 SystemUI 的。SystemUI,在源码目录中位于: framework/base/packages 目录下...
    99+
    2023-09-11
    android
  • Android系统启动流程
    Android系统完整的启动过程,从系统层次角度可分为 Linux 系统层、Android 系统服务层、Zygote进程模型三个阶段;从开机到启动 Home Launcher 完成具体的任务细节可分为七个步骤,下面就从具体的细节来解读 An...
    99+
    2023-09-03
    android
  • Android Activity 启动流程 二:setContentView
    关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、商业变现、人工智能等,希望大家多多支持。 目录 一、概...
    99+
    2023-09-03
    android 启动流程 java 面试 setcontentview
  • Android Activity启动流程刨析
    目录前言一、Binder的基本理解二、Activity启动的双向IPC过程三、AMS服务注册前言 上篇文章写到 Service 的启动过程; 相对来说Activity的启动过程比Se...
    99+
    2022-11-13
    Android Activity启动 Android Activity
  • Android zygote启动流程详解
    目录对zygote的理解作用启动流程启动入口脚本讲解启动过程App_main::mainAndroidRuntime::start对zygote的理解 在Android系统中,zy...
    99+
    2024-04-02
  • Android Service启动流程刨析
    强调一下阅读系统源码,起码要对进程间通信要了解,对binder机制非常非常清楚,binder就是指南针,要不然你会晕头转向;强行阅读,就容易睡着。 Service启动先来一张图感受一...
    99+
    2022-11-13
    Android Service Android Service启动
  • android启动流程是什么
    Android启动流程是指从手机开机到系统完全启动的过程。具体的流程如下:1. 电源按下:当用户按下电源键时,电源管理芯片会向处理器...
    99+
    2023-10-11
    android
  • 详解Android Activity的启动流程
    目录前言 简要 1.Launcher向AMS发送启动Activity 2.AMS启动Activity并通知Launcher进入Paused状态 3.新的进程启动,ActivityTh...
    99+
    2024-04-02
  • android activity启动流程是什么
    Android Activity的启动流程如下:1. 调用`startActivity()`方法或者`startActivityFo...
    99+
    2023-08-08
    android activity
  • android launcher启动流程是什么
    Android Launcher的启动流程如下:1. 用户点击设备上的Home按钮或者通过其他方式启动Launcher应用。2. 系...
    99+
    2023-10-20
    android
  • Android Service启动绑定流程详解
    目录前言一、Service 的启动流程二、Service的绑定三、Service的Context总结前言 本文基于Android 11,参考《Android进阶解密》一书资料。了解...
    99+
    2023-03-08
    Android  Service启动绑定流程 Android Service
  • Android framework ATMS启动流程是什么
    这篇文章主要介绍“Android framework ATMS启动流程是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Android framework ...
    99+
    2023-07-05
  • 浅析Android 11 Gnss定位流程
    架构设计图 APP层 我们在做app开发定位功能时,除了使用高德/百度定位,通常还使用原生的LocationManager来获取定位数据,核心代码如下: //代码5private LocationListener location...
    99+
    2023-09-16
    android android studio java
  • Android开发InputManagerService创建与启动流程
    目录前言启动流程创建输入系统启动输入系统输入系统就绪结束前言 之前写过几篇关于输入系统的文章,但是还没有写完,后来由于工作的变动,这个事情就一直耽搁了。而现在,在工作中,遇到输入系统...
    99+
    2022-11-13
    Android InputManagerService Android InputManagerService
  • 详解Android广播Broadcast的启动流程
    Android中的广播是一种用于应用程序之间通信的机制。它允许应用程序发送和接收系统级或应用程序级的广播消息。当一个广播被发送时,系...
    99+
    2023-08-11
    Android
  • Android Service启动绑定流程是什么
    这篇文章主要介绍了Android Service启动绑定流程是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Android Service启动绑定流程是什么文章都会有所收获,下面我们一起...
    99+
    2023-07-05
  • Android应用程序的启动流程是什么
    本篇内容介绍了“Android应用程序的启动流程是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!应用进程的启动流程本文基于Android...
    99+
    2023-07-05
  • Android okhttp的启动流程及源码解析
    目录前言 什么是OKhttp OkHttp是如何做网络请求的 1.它是如何使用的? 1.1 通过构造者模式添加 url,method,header,body 等完成一个请求的信息 R...
    99+
    2024-04-02
  • Android广播Broadcast的启动流程是什么
    这篇“Android广播Broadcast的启动流程是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Android广播B...
    99+
    2023-07-05
  • AndroidframeworkATMS启动流程
    目录1 前言(1)ATMS 创建流程(2)ATMS 初始化2 ATMS 启动流程(1)main(2)run(3)startBootstrapServices(4)startServi...
    99+
    2023-03-08
    Android framework ATMS启动 Android framework ATMS
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作