Android8.1原生systemUI导致framwork全局符号表溢出问题

一、问题描述:

10台机器进行某项自动化测试,一轮5天,发现一台机器没有完成测试就停止了。

二、分析过程:

1. 拿到log,可以快速地定位到system_server发生了crash导致android层重启,且直接原因是全局引用表溢出,虚拟机dump信息如下:

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256] JNI ERROR (app bug): global reference table overflow (max=51200)

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256] global reference table dump:

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]  Last 10 entries (of 51200):

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]    51199: 0x12e3ba60 com.android.server.content.ContentService$ObserverNode$ObserverEntry

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]    51198: 0x12d93760 com.android.server.am.ServiceRecord

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]    51197: 0x12d8fa20 java.lang.ref.WeakReference (referent is a android.os.BinderProxy)

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]    51196: 0x12e391b8 java.lang.ref.WeakReference (referent is a android.os.BinderProxy)

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]    51195:0x12c4db58 com.android.server.am.ServiceRecord

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]    51194: 0x12dc3e78 java.lang.ref.WeakReference (referent is a android.os.BinderProxy)

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]    51193: 0x12e3b560 java.lang.ref.WeakReference (referent is a android.os.BinderProxy)

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]    51192: 0x12e38718 java.lang.ref.WeakReference (referent is a android.os.BinderProxy)

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]    51191: 0x12dc3fc0 java.lang.ref.WeakReference (referent is a android.os.BinderProxy)

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]    51190: 0x12fc8b60 com.android.server.am.ServiceRecord

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]  Summary:

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]    24615 of java.lang.ref.WeakReference (24615 unique instances)

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]    23622 of com.android.server.content.ContentService$ObserverNode$ObserverEntry (23622 unique instances)

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]      758 of android.os.Binder (758 unique instances)

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]      485 of com.android.server.notification.NotificationManagerService$StatusBarNotificationHolder (485 unique instances)

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]      468 of com.android.server.am.ServiceRecord (468 unique instances)

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]      319 of java.lang.Class (239 unique instances)

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]      181 of java.nio.DirectByteBuffer (170 unique instances)

08-25 18:41:14.285  955  4704 F zygote64: indirect_reference_table.cc:256]      119 of android.os.RemoteCallbackList$Callback (119 unique instances)

由上面dump的关键信息可以得到,虚拟机全局引用表对象引用个数限制为51200个;最后在创建一个ObserverEntry对象;而引用数最多的两个对象是ObserverEntry和WeakReference(怀疑是binder实体的弱引用,ObserverEntry代表一个内容监听器在framwork的实例,是用来回调到app端的,所以ObserverEntry和WeakReference 数量接近)。这里只能从ObserverEntry对象入手继续分析,结合ContentService的实现,怀疑有应用反复注册了内容监听器而没有在适当的时候释放。需要现场来进行定位确认,但是复现问题的机器现场已经丢失,只能拿做完测试但未复现问题、且没重启过的机器来做现场调试。

2.拿到一台测试后没有重启的机器。既然ObserverEntry的对象这么多,那么就dumpsys content看看ContentService的信息,想进一步了解其内部信息可以看看ContentService的registerContentObserver、unregisterContentObserver和dump函数的逻辑。这里不详细说,直接看结果。这里有大量的监听器,且注册者都是pid=1266的进程。通过ps命令查看或者log,发现pid 1266是SystemUI。

……

settings/global/always_on_display_constants:pid=1266 uid=10044 user=-1 target=17dc217

settings/global/always_on_display_constants:pid=1266 uid=10044 user=-1 target=878d604

……

pid 1266: 10532 observers

……

3. 现在我们基本确定是SystemUI反复注册了always_on_display_constants的监听器而没有释放,通过监听器内容搜索SystemUI的代码,找到其中注册always_on_display_constants监听器的逻辑,发现在启动DozeService的时候总共会注册三个always_on_display_constants的监听器,但是没有看到有注销的地方。这应该就是ObserverEntry对象泄露的原因。

4.那DozeService什么时候启动,又是什么时候退出呢?再看看log,有大量的启动停止DozeService的log记录,启动记录7609条,每启动退出一次泄露3个,在能看到的log记录总共就泄露22827个。非常接近前面虚拟机dump出来的引用表信息ObserverEntry的数量。

08-21 05:41:56.013 955 4704 I PowerManagerService: Going to sleep due to power button (uid 1000)...

......

08-21 05:41:56.713  955  974 I DreamController: Starting dream: name=ComponentInfo{com.android.systemui/com.android.systemui.doze.DozeService}, isTest=false, canDoze=true, userId=0

......

08-21 05:42:01.530  955  1520 I PowerManagerService: Waking up from dozing (uid=1000 reason=android.policy:POWER)...

......

08-21 05:42:01.829  955  974 I DreamController: Stopping dream: name=ComponentInfo{com.android.systemui/com.android.systemui.doze.DozeService}, isTest=false, canDoze=true, userId=0

自动化测试通常都比较耗时,问题不好验证,最好是找到复现问题的步骤。这个问题也比较明显,DozeService会在灭屏的时候启动,亮屏的时候退出,这是framework的逻辑,这里不详细分析。我们通过亮灭屏的动作和dumpsys content看看我们前面的猜测是不是对的。灭屏后亮屏可以看到observer的对象增加3,而增加的3个都是always_on_display_constants

4. 和测试的同事确认这个自动化测试用例中是否有亮灭屏的动作,得到了肯定的答复,是客户要求的测试用例!这样我们就有了复现问题的方法,后续修改验证也就方便了许多。

三、修改方案

diff --git a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java

index debda21..9b69031 100644

--- a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java

+++ b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java

@@ -102,6 +102,14 @@

        mSettingsObserver.observe();

    }

+    public void clear() {

+        if (mSettingsObserver != null) {

+            //mSettingsObserver.clearObserve();

+            ContentResolver resolver = mContext.getContentResolver();

+            resolver.unregisterContentObserver(mSettingsObserver);

+        }

+    }

+

    private int[] parseIntArray(final String key, final int[] defaultArray) {

        final String value = mParser.getString(key, null);

        if (value != null) {

@@ -130,7 +138,12 @@

                    false, this, UserHandle.USER_ALL);

            update(null);

        }

-

+/*

+        void clearObserve() {

+            ContentResolver resolver = mContext.getContentResolver();

+            resolver.registerContentObserver(this);

+        }

+*/

        @Override

        public void onChange(boolean selfChange, Uri uri) {

            update(uri);

diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java

index 8ec6afc..7d3dc3f 100644

--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java

+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java

@@ -129,6 +129,13 @@

        mParts = parts;

    }

+    /** clear some reference stored in framework-system_server */

+    public void clear() {

+        for (Part p : mParts) {

+            p.clear();

+        }

+    }

+

    /**

      * Requests transitioning to {@code requestedState}.

      *

@@ -348,6 +355,8 @@

          */

        void transitionTo(State oldState, State newState);

+        default void clear() {}

+

        /** Dump current state. For debugging only. */

        default void dump(PrintWriter pw) {}

    }

diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java

index 58f1448..f7f49225 100644

--- a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java

+++ b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java

@@ -50,6 +50,13 @@

        }

    }

+    @Override

+    public void clear() {

+        if (mPolicy != null) {

+            mPolicy.clear();

+        }

+    }

+

    private void onTimeout() {

        mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSED);

    }

diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java

index 4bb4e79..f31d3c6 100644

--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java

+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java

@@ -38,6 +38,7 @@

    private final Sensor mLightSensor;

    private final int[] mSensorToBrightness;

    private final int[] mSensorToScrimOpacity;

+    private final AlwaysOnDisplayPolicy mPolicy;

    private boolean mRegistered;

    private int mDefaultDozeBrightness;

@@ -58,16 +59,31 @@

        mDefaultDozeBrightness = defaultDozeBrightness;

        mSensorToBrightness = sensorToBrightness;

        mSensorToScrimOpacity = sensorToScrimOpacity;

+

+        mPolicy = null;

    }

    @VisibleForTesting

    public DozeScreenBrightness(Context context, DozeMachine.Service service,

            SensorManager sensorManager, Sensor lightSensor, DozeHost host,

            Handler handler, AlwaysOnDisplayPolicy policy) {

+/*

        this(context, service, sensorManager, lightSensor, host, handler,

                context.getResources().getInteger(

                        com.android.internal.R.integer.config_screenBrightnessDoze),

                policy.screenBrightnessArray, policy.dimmingScrimArray);

+*/

+        mContext = context;

+        mDozeService = service;

+        mSensorManager = sensorManager;

+        mLightSensor = lightSensor;

+        mDozeHost = host;

+        mHandler = handler;

+

+        mDefaultDozeBrightness = context.getResources().getInteger(com.android.internal.R.integer.config_screenBrightnessDoze);

+        mSensorToBrightness = policy.screenBrightnessArray;

+        mSensorToScrimOpacity = policy.dimmingScrimArray;

+        mPolicy = policy;

    }

    @Override

@@ -94,6 +110,13 @@

    }

    @Override

+    public void clear() {

+        if (mPolicy != null) {

+            mPolicy.clear();

+        }

+    }

+

+    @Override

    public void onSensorChanged(SensorEvent event) {

        Trace.beginSection("DozeScreenBrightness.onSensorChanged" + event.values[0]);

        try {

diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java

index 91cde37..000e47a 100644

--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java

+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java

@@ -133,6 +133,12 @@

        return null;

    }

+    public void clear() {

+        if (mProxSensor != null) {

+            mProxSensor.clear();

+        }

+    }

+

    public void setListening(boolean listen) {

        for (TriggerSensor s : mSensors) {

            s.setListening(listen);

@@ -234,6 +240,12 @@

            updateRegistered();

        }

+        public void clear() {

+            if (mPolicy != null) {

+                mPolicy.clear();

+            }

+        }

+

        private void updateRegistered() {

            setRegistered(mRequested && !mCooldownTimer.isScheduled());

        }

diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java

index 98b1106..b147f97 100644

--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java

+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java

@@ -57,6 +57,16 @@

        mDozeMachine = new DozeFactory().assembleMachine(this);

    }

+    /** {@inheritDoc} */

+    @Override

+    public void onDestroy() {

+        Log.d(TAG, "onDestroy() being called when DreamController stop this service");

+        if (mDozeMachine != null) {

+            mDozeMachine.clear();

+        }

+        super.onDestroy();

+    }

+

    @Override

    public void onPluginConnected(DozeServicePlugin plugin, Context pluginContext) {

        mDozePlugin = plugin;

diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java

index f7a258a..ea6ae4d 100644

--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java

+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java

@@ -212,6 +212,13 @@

        }

    }

+    @Override

+    public void clear() {

+        if (mDozeSensors != null) {

+            mDozeSensors.clear();

+        }

+    }

+

    private void checkTriggersAtInit() {

        if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR

                || mDozeHost.isPowerSaveActive()

四、验证方法

由于问题比较清晰,我们直接测试20000次”休眠唤醒”的动作来对比验证。准备两台机器,一台刷老版本;一台刷修改后的版本。验证结果为老版本8000次左右就发生了crash,而修改后的版本20000次后系统依然正常。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容