In-depth understanding of the status bar

This article is an "in-depth understanding of Android Volume III" in-depth understanding of the status bar reading notes in the chapter.

SystemUI frameworks location in the source code / base / packages / SystemUI in.


First, acquaintance SystemUI

Mainly about the status and navigation bars, all running in the SystemUIService

1, SystemUIService boot process

In ServerThread responsible for starting system services, call 

ActivityManagerService.self().systemReady(new Runnable() {
            public void run() {
               ...

                if (!headless) startSystemUi(contextF);

Then start SystemUIService

 static final void startSystemUi(Context context) {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.android.systemui",
                    "com.android.systemui.SystemUIService"));
        //Slog.d(TAG, "Starting service: " + intent);
        context.startServiceAsUser(intent, UserHandle.OWNER);
    }


  Will start in SystemUIServie

 /**
     * The class names of the stuff to start.
     */
    final Object[] SERVICES = new Object[] {
            0, // system bar or status bar, filled in below.
            com.android.systemui.power.PowerUI.class,
            com.android.systemui.media.RingtonePlayer.class,
            com.android.systemui.settings.SettingsUI.class,
        };


In SystemUI, the separation scheme is implemented by PhoneStatusBar, achieved by integration of the layout scheme is TabletStatusBar. Essential function of both is the same, i.e., to provide virtual keys, notification information, and differ only in the different layout, and the this custom behavior derived from it.

Change in the screen is within 720 dp, using separate layout scheme, the judgment condition in PhoneWindowManager.setInitialDisplaySize () in.



Second, create a status bar

Start the status and navigation bars are used PhoneStatusBar.start () completed.

1, the creation process

In  PhoneStatuBar.addStatusBarWindow () in
    private void addStatusBarWindow() {
        // Put up the view
        // 状态栏的高度,默认是 25dp, 位置在 frameworks/base/core/res/res/values/dimen.xml 中
        // R.dimen.status_bar_height
        final int height = getStatusBarHeight();

        // Now that the status bar window encompasses the sliding panel and its
        // translucent backdrop, the entire thing is made TRANSLUCENT and is
        // hardware-accelerated.
        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                height,
                WindowManager.LayoutParams.TYPE_STATUS_BAR, // 窗口类型
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE   // 状态栏不接受按键事件
                    // 状态栏接受导致设备唤醒的事件
                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                    // 允许状态栏支持触摸事件序列的拆分
                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
                PixelFormat.TRANSLUCENT);   // 状态栏的 Surface 像素格式为支持透明度
        // 启动硬件加速
        lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;

        lp.gravity = getStatusBarGravity();
        lp.setTitle("StatusBar");
        lp.packageName = mContext.getPackageName();

        // 创建 StatusBar 和 导航栏
        makeStatusBarView();
        // 添加到窗口,创建过程完成
        mWindowManager.addView(mStatusBarWindow, lp);
    }

2. Layout

In makeStatusBarView () method, the layout file is R.layout.super_status_bar.xml 
1. StatusBarWindowView root layout of controls, inheritance FrameLayout. Direct child controls
The layout @ layout / status_bar described, it is usually we see the status bar;

. @ PanelHolder This is the inheritance of the shutter state FrameLayout
@ Layout / status_bar_expended is the list of notifications panel
@ Layout / quick_settings quick setting panel

2. mStatusBarView directly controls three
. @id/notification_lights_out
一个 ImageView ,一般不显示。当在 SystemUIVisibility 中添加 SYSTEM_UI_FLAG_LOW_PROFILE 使状态栏与导航栏进入低辩识度模式(状态栏不会显示任何信息,只会在黑色背景中显示一个灰度圆点而已),灰度圆点就是 id/notification_lights_out.

. @id/status_bar_contents 
一个 LinearLayout ,状态栏上各种信息的显示场所。包含
@id/notification_icon_area
一个 LinearLayout, 包含类型为 BarIconView 的 @id/moreIcon 以及一个 IconMeger 的@id/notificaitonIcons. IconMeger 继承 LinearLayout, 当它在 onLayout() 过程中,发现内部的StatusBarView 的总宽度超过了它自身的宽度,会设置 @id/moreIcon 可见;

@id/system_icon_area
一个 LinearLayout,显示通知信息的图标以为的四种信息
@id/clock 显示时间信息
@id/battery 显示电量信息
@id/signal_cluster 显示信号信息
@id/statusIcons 负责容纳系统状态区的图标

@id/ticker
一个 LinearLayout ,包含一个 ImageSwitcher 和一个 TickerView
用于当一条新通知到来时(例如一条新的短信),在状态栏上以动画方式逐行显示通知的内容。

3. 重要变量

. mStatusBarWindow, 整个状态栏的根控件。它包含两棵子控件树,分别是常态下的状态栏和下拉卷帘;
   . mStatusBarView, 常态下的状态栏, 三棵控件树分别对应低辨识度模式、Ticker 以及常态三个工作状态;

. mNotificationIcons, 继承 LinearLayout 的 IconMeger 控件,负责容纳通知图标,当宽度不足的时候,会将@id/moreIcons 设为可见,告知用户存在未显示通知;

. mTickerView, 实现当新通知来到时的动画效果,使得用户可以在无须下拉卷帘的情况下了解新通知的内容;

. mStatusIcons, 一个 LinearLayout , 它是系统状态图标区,负责容纳系统状态图标;

.mPile, 一个 NotificationRowLayout, 它作为通知列表的容器被保存在下拉卷帘中。显示通知的详细信息。


三、状态栏显示的通知

1. 通知信息:
位于信息栏左侧,可以下拉卷帘显示更加详细的信息;可通过 StatusBarManagerService 提供接口添加或者移除信息;

2. 时间信息:
一个名为 Clock 的继承自 TextView 的控件;
监听的广播:
ACTION_TIME_TICK, ACTION_TIME_CHANGE, ACTION_TIMEZONE_CHANGED, ACTION_CONFIGURATION_CHANGED.

3. 电量信息:
一个 ImageView, BatteryController 类管理;BatteryController 通过监听 android.intent.action.BATTERY_CHANGED 广 播,从 BattryService 中获取电量信息;

4. 信号信息:
左侧一系列 ImageView, 用于显示系统当前 WiFi, 移动网络的信号的状态;
NetworkController 类管理;
监听的广播:
WIFI_STATE_CHANGED_ACTION, ACTION_SIM_STATE_CHANGED, ACTION_AIRPLANE_MODE_CHANGED.

5. 系统状态图标区:
用一系列图标标识系统当前的状态;
通过 StatusBarManagerService 通过 setIcon() 接口为外界提供修改系统状态图标区的途径;

四、SystemUI 的体系结构

.

1. SystemUIService 

一个普通的 Android Service ,它以一个容器的角色运行于 SystemUI 进程中。

2. CommandQueue

运行在 SystemUI 进程中;

继承 IStatusBar.Stub, 是 IStatusBar 的 Bn 段,是 IStatusBarService 与 BaseStatusBar 进行沟通的桥梁;

3. IStatusBarService;

运行在 system_server 进程中;

即系统服务 StatusBarManagerService 是状态栏、导航栏向外界提供服务的前端接口;它接受操作状态栏、导航栏的请求 并转给 BaseStatusBar;

4.  StatusBarManagerService 

运行在 system_server 进程中;

是 IStatusBarService 的实现者, 在 ServerThread.run() 方法中启动;

它是 SystemUI 中状态栏和导航栏在 system_server 中的代理。所有对状态栏或者导航栏来有需求的对象都可以通过获取 StatusBarManagerService 的实例或 Bp 端达到其目的。只不过使用者必须拥有能够完成操作的相应权限;

它保存了状态栏/导航栏所需的信息副本,用于在 SystemUI 意外退出之后的恢复;


 5.IStatusBar

运行在 SystemUI 进程中;

是 SystemUI 中 CommandQueue 联系 StatusBarManagerSerevice 与 BaseStatusBar 的桥梁;


6. SystemUI also includes achieved ImageWallpaper, RecentPanel TakeScreenshotService and other functions. They are Service, Activity and other standard Android application components, independent of each other.


Fifth, the notification display process information

Let's build an example of a display notifications

    private void sendNotification(){
        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        Intent intent = new Intent(this, Main2Activity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
        Notification notification = new  Notification.Builder(this)
                .setAutoCancel(true)
                .setTicker("测试新通知")
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle("新通知")
                .setContentText("测试测试测出测试")
                .setWhen(System.currentTimeMillis())
                .setContentIntent(pendingIntent)
                .build();
        manager.notify(11232, notification);
    }
Show results

Notification display information beginning from NotificationManager.notify (int id, Notification notification)

1, in NotificationManagerService.enqueueNotificationWithTag () in

Call checkCallerIsSystemOrSameApp (...) security checks

checkCallerIsSystemOrSameApp () gets the UID notify the submitter of the information and get in distribution and PackageManager have to specify the package UID name of the application for comparison. If not, then the malicious software attempts to tamper with other applications sent by way of fraudulent use of the name of the package notification information, it will throw an exception, prohibit such behavior.


Restrictions Each application can only be submitted up to 50 notifications, preventing malicious software or notification by a large number of registered lead to system failures

// Limit the number of notifications that any given package except the android
        // package can enqueue.  Prevents DOS attacks and deals with leaks.
        if (!isSystemNotification) {
           ..
                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
                            ..
                            return;
                        }
                    }
                }
            }
        }

. According to rate the importance of information, priority fields, if the score is too low, this notification message will be ignored;


pkg, tag, id in NotificationManagerService in together constitute a unique identification notification.


2. PhoneStatusBar.addNotification () in

<span style="font-size:14px;">public void addNotification(IBinder key, StatusBarNotification notification) {
        if (DEBUG) Slog.d(TAG, "addNotification score=" + notification.getScore());
        // 1. 将通知内容添加到控件树中, 在 BaseStatusBar 中创建 view
        StatusBarIconView iconView = addNotificationViews(key, notification);
        if (iconView == null) return;

        boolean immersive = false;
        try {
            immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
            if (DEBUG) {
                Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
            }
        } catch (RemoteException ex) {
        }

        // 2. 为新的通知启动 fullScreenIntent 或进行 ticker
        if (notification.getNotification().fullScreenIntent != null) {
            // Stop screensaver if the notification has a full-screen intent.
            // (like an incoming phone call)
            awakenDreams();

            // not immersive & a full-screen alert should be shown
            if (DEBUG) Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
            try {
                notification.getNotification().fullScreenIntent.send();
            } catch (PendingIntent.CanceledException e) {
            }
        } else {
            // usual case: status bar visible & not immersive

            // show the ticker if there isn't an intruder too
            if (mCurrentlyIntrudingNotification == null) {
                tick(null, notification, true);
            }
        }

        // 3. 更新周边控件
        // Recalculate the position of the sliding windows and the titles.
        setAreThereNotifications();
        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
    }</span>

3. BaseStatusBar.addNotificationViews (...) in

    protected StatusBarIconView addNotificationViews(IBinder key,
            StatusBarNotification notification) {
        if (DEBUG) {
            Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
        }
        // Construct the icon.
        // 1. 创建一个 StatusBarIconView ,用于显示通知的图标;
        final StatusBarIconView iconView = new StatusBarIconView(mContext,
                notification.getPackageName() + "/0x" + Integer.toHexString(notification.getId()),
                notification.getNotification());
        iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);

        // 2. 创建 StatusBarIcon 实例, 用于保存在 Notification 实例中与通知图标相关的信息;
        final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
                notification.getUser(),
                    notification.getNotification().icon,
                    notification.getNotification().iconLevel,
                    notification.getNotification().number,
                    notification.getNotification().tickerText);
        if (!iconView.set(ic)) {
            handleNotificationError(key, notification, "Couldn't create icon: " + ic);
            return null;
        }
        // Construct the expanded view.
        // 3. 创建 NotificaitonData.Entry 实例
        NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
        // 4. 创建通知在下拉卷帘中的控件树,并将其添加到 mPile 中
        if (!inflateViews(entry, mPile)) {
            handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
                    + notification);
            return null;
        }

        // Add the expanded view and icon.
        //  5.  保存 entry 到 mNotificaitonData 中
        int pos = mNotificationData.add(entry);
        if (DEBUG) {
            Slog.d(TAG, "addNotificationViews: added at " + pos);
        }

        // 6. 更新通知在下拉卷帘中的展开状态;
        updateExpansionStates();
        // 7. 更新状态栏中所有通知图标的显示状态。
        updateNotificationIcons();

        return iconView;
    }

inflateViews (...) method described in the configuration of the pull-down shutter control tree, reference P445. 

通知的详细信息由 R.layout.status_bar_notification_row 显示在 mPile 中。


六、系统状态图标的显示

系统状态图标区的意图由一个字符串描述,意图列表在 frameworks/base/core/res/res/values/config.xml 中 config_statusBarIcons 定义。

显示过程

1. 在 StatusBarManagerService.setIcon() 中

调用 enforceStatusBar() 方法,设置者必须拥有签名级系统权限 android.permission.STATUS_BAR 才能设置系统状 态图标。


2. 大多数系统图标通过 PhoneStatusBarPolicy 类设置

PhoneStatusBarPolicy 在 PhoneStatusBar.start() 方法中创建,通过监听一些广播,随后调用相应的方法,最后调用 StatusBarManager.setIcon 进行修改图标。

监听的广播

        // listen for broadcasts
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_ALARM_CHANGED);
        filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
        filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
        mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);

 至此,关于导航栏的内容基本结束了。




发布了58 篇原创文章 · 获赞 20 · 访问量 9万+

Guess you like

Origin blog.csdn.net/yxhuang2008/article/details/52502385