处理华为Framework层中curosr和空指针问题(反编译ROM和Hook动态代理)

Posted 新根

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了处理华为Framework层中curosr和空指针问题(反编译ROM和Hook动态代理)相关的知识,希望对你有一定的参考价值。

最近一直忙着解决公司游戏的Bug,但有些问题是顽固分子,许多个迭代都没解决掉,但不处理总觉有一根刺,在扎着肉。

本篇是记录如何处理手机系统ROM中Framework中报错,可以适用不同的手机厂商。

bugly上的问题:

android.database.CursorWindowAllocationException
Cursor window could not be created from binder.
android.database.CursorWindow.<init>(CursorWindow.java:137)
android.database.CursorWindow.<init>(CursorWindow.java)
android.database.CursorWindow$1.createFromParcel(CursorWindow.java:685)
android.database.CursorWindow$1.createFromParcel(CursorWindow.java:684)
android.database.BulkCursorDescriptor.readFromParcel(BulkCursorDescriptor.java:75)
android.database.BulkCursorDescriptor$1.createFromParcel(BulkCursorDescriptor.java:34)
android.database.BulkCursorDescriptor$1.createFromParcel(BulkCursorDescriptor.java:32)
android.content.ContentProviderProxy.query(ContentProviderNative.java:424)
android.content.ContentResolver.query(ContentResolver.java:541)
android.content.ContentResolver.query(ContentResolver.java:476)
com.huawei.android.hwaps.OperateExperienceLib.isGameInfoExist(OperateExperienceLib.java:497)
com.huawei.android.hwaps.OperateExperienceLib.saveAPSResult(OperateExperienceLib.java:429)
com.huawei.android.hwaps.EventAnalyzed.setAdaptFPS(EventAnalyzed.java:1373)
com.huawei.android.hwaps.EventAnalyzed.processAnalyze(EventAnalyzed.java:1726)
android.view.HwNsdImpl.adaptPowerSave(HwNsdImpl.java:1182)
android.view.ViewRootImpl$EarlyPostImeInputStage.processPointerEvent(ViewRootImpl.java:4648)
android.view.ViewRootImpl$EarlyPostImeInputStage.onProcess(ViewRootImpl.java:4617)
android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4258)
android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6690)
android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6664)
android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6625)
android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6819)
android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:192)
android.os.MessageQueue.nativePollOnce(Native Method)
android.os.MessageQueue.next(MessageQueue.java:356)
android.os.Looper.loop(Looper.java:138)
android.app.ActivityThread.main(ActivityThread.java:6623)
java.lang.reflect.Method.invoke(Native Method)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)

从代码报错点来看是,cursor 过多未关闭,存在泄漏,产生的原因可能是cursor未关闭或者fd 过多或者是内存不足。

因报错点发生在华为ROM的framework层,因此要先考虑获取到对应的framework.dex, 查看代码调用流程,再来确定解决方案。

设备信息:

设备机型系统版本ROM
WAS-AL00Android 8.0.0,level 26HuaWei/EMOTION

1.前期准备:反编译华为Rom系统

可考虑下载华为固件Rom 或者通过adb pull 获取该机型的framework.dex, 详细操作,请阅读Android反编译之各大手机厂商的系统(adb pull和Rom包)

经过一些列操作,获取到framework有关的dex,如下图所示:

2.源码分析定位

通过gui 工具打开华为rom 的framework 层源码。

先从ViewRootImpl#EarlyPostImeInputStage的onProcess()开始入手,追查起。

android/view/ViewRootImpl:

    final class EarlyPostImeInputStage extends InputStage 

        protected int onProcess(QueuedInputEvent q) 
            if (q.mEvent instanceof KeyEvent) 
                return processKeyEvent(q);
            
            if ((q.mEvent.getSource() & 2) != 0) 
                return processPointerEvent(q);
            
            return 0;
        

        private int processPointerEvent(QueuedInputEvent q) 
            MotionEvent event = q.mEvent;
            if (ViewRootImpl.this.mTranslator != null) 
                ViewRootImpl.this.mTranslator.translateEventInScreenToAppWindow(event);
            
			    //若是rom 支持aps功能和包名和进程名匹配的情况下
            if (HwFrameworkFactory.getHwNsdImpl().isSupportAps() && HwFrameworkFactory.getHwNsdImpl().isGameProcess(ViewRootImpl.this.mContext.getPackageName())) 
                HwFrameworkFactory.getHwNsdImpl().initAPS(ViewRootImpl.this.mView.getContext(), ViewRootImpl.this.mView.getResources().getDisplayMetrics().widthPixels, Process.myPid());
                HwFrameworkFactory.getHwNsdImpl().adaptPowerSave(event);
            
            int action = event.getAction();
            if (HwFrameworkFactory.getHwViewRootImpl().filterDecorPointerEvent(ViewRootImpl.this.mContext, event, action, ViewRootImpl.this.mWindowAttributes, ViewRootImpl.this.mDisplay)) 
                return 1;
            
			//... 
            return 0;
        
    


先来看下getHwNsdImpl()获取的对象,才能更好的了解源码调用过程。

android/common/HwFrameworkFactory中:

  public static IHwNsdImpl getHwNsdImpl() 
        Factory obj = getImplObject();
        if (obj != null) 
            return obj.getHwNsdImpl();
        
        return null;
  
  private static Factory getImplObject() 
        if (obj != null) 
            return obj;
        
        synchronized (mLock) 
            try // 反射获取真正的HwFrameworkFactory实现类
                obj = (Factory) Class.forName("huawei.android.common.HwFrameworkFactoryImpl").newInstance();
             catch (Exception e) 
                Log.e(TAG, ": reflection exception is " + e);
            
        
        //...
        return null;
    

HwFrameworkFactory 是一个封装的api 类,用于静态方法/工厂类方式,间接调用各种api。

接下来,继续看下huawei/android/common/HwFrameworkFactoryImpl类中的getHwNsdImpl()

    public HwNsdImpl getHwNsdImpl() 
        return HwNsdImpl.getDefault();
    

接下来看下android/view/HwNsdImpl

   public static HwNsdImpl getDefault() 
        if (mInstance == null) 
            mInstance = new HwNsdImpl();
        
        return mInstance;
    

经过一些列的追踪,可以了解到ViewRootImpl对象中调用的是HwNsdImpl中的方法。

接下来看下,isGameProcess()isSupportAps()adaptPowerSave()等几个方法。

2.1 华为framework 统计游戏fps 的条件

    public boolean isSupportAps()  
        return SystemProperties.getInt("sys.aps.support", 0) > 0;
    

从上面代码可知,isSupportAps()是检测系统rom 是否支持aps功能。

接下来看下isGameProcess()

 private static IEventAnalyzed mEventAnalyzed = null;

    public boolean isGameProcess(String pkgName) 
        createEventAnalyzed();
        if (mEventAnalyzed != null) 
            return mEventAnalyzed.isGameProcess(pkgName);
        
        return false;
    
    public synchronized void createEventAnalyzed() 
        if (mEventAnalyzed == null) 
            mEventAnalyzed = HwapsWrapper.getEventAnalyzed();
        
    

接下来看下, com/huawei/android/hwaps/HwapsWrapper类中getEventAnalyzed():

    public static IEventAnalyzed getEventAnalyzed() 
        IHwapsFactory factory = getHwapsFactoryImpl();
        return factory != null ? factory.getEventAnalyzed() : null;
    

    private static synchronized IHwapsFactory getHwapsFactoryImpl() 
        IHwapsFactory iHwapsFactory;
        synchronized (HwapsWrapper.class) 
            if (mFactory != null) 
                iHwapsFactory = mFactory;
             else 
                try 
                    mFactory = (IHwapsFactory) Class.forName("com.huawei.android.hwaps.HwapsFactoryImpl").newInstance();
                 catch (ClassNotFoundException e) 
                    Log.e(TAG, "reflection exception is " + e);
                 catch (InstantiationException e2) 
                    Log.e(TAG, "reflection exception is " + e2);
                 catch (IllegalAccessException e3) 
                    Log.e(TAG, "reflection exception is " + e3);
                
                if (mFactory == null) 
                    Log.e(TAG, "failes to get HwapsFactoryImpl");
                
                iHwapsFactory = mFactory;
            
        
        return iHwapsFactory;
    

从上面代码可知, HwapsWrapper是一个封装api的类, 其真正调用是HwapsFactoryImpl类。

com/huawei/android/hwaps/HwapsFactoryImpl

public class HwapsFactoryImpl implements IHwapsFactory 

    //...
    public IEventAnalyzed getEventAnalyzed() 
        return new EventAnalyzed();
    

经过一些列的波折,最终调用链是ViewRootImpl->IHwNsdImpl->EventAnalyzed类的方法:

com/huawei/android/hwaps/EventAnalyzed

    //检查下是否是原始进程对应的包名
    private boolean isIdentifyProcess(String strPkgName) 
        if (!this.mHasIdentifyProcess) 
            if (SystemProperties.get("debug.aps.process.name", "").equals(strPkgName)) 
                this.mIsGameProcess = true;
             else 
                this.mIsGameProcess = false;
            
            this.mHasIdentifyProcess = true;
        
        return this.mIsGameProcess;
    

    public boolean isGameProcess(String pkgName) 
        if (this.mHasIdentifyProcess && !this.mIsGameProcess) 
            return this.mIsGameProcess;
        
        if (isAPSReady()) 
            return isIdentifyProcess(pkgName); // 通常下,app主进程 是返回true
        
        return false;
    

从上面可见,若pkgName是app 主进程名,是返回true的。

这里注意点, EventAnalyzed 是com/huawei/android/hwaps/IEventAnalyzed接口的实现类,后面会用到。

public interface IEventAnalyzed 

    //......
    boolean isGameProcess(String str);
    void processAnalyze(int i, long j, int i2, int i3, int i4, long j2);
    void setHasOnPaused(boolean z);

2.2 华为 framework层是如何存储游戏的fps功能

HwNsdImpl#adaptPowerSave()是调用EventAnalyzed#processAnalyze(),因此,接下来看下processAnalyze()

    public void processAnalyze(int aciton, long eventTime, int x, int y, int pointCount, long downTime) 
       //...
	   else if (CUST_APP_TYPE != mGameType) 
            //...
            setAdaptFPS(gametype, openGLType, this.mLevel);
        
    
	
	public void setAdaptFPS(int gameType, int openGLType, int level) 
            //...
            else if (!this.mHasSetFPS) 
                mGameType = gameType;
                int recommendFPS = computeRecommendFPS(gameType, openGLType, level);
                if (gameType == 8 || gameType == 9) 
                    this.mOperateExLib.saveAPSResult(gameType, recommendFPS, recommendFPS);
                 else 
                    this.mOperateExLib.saveAPSResult(gameType, recommendFPS, mMinFps);
                
				//.....
            
    

接下来看下com/huawei/android/hwaps/OperateExperienceLibsaveAPSResult():

    public void saveAPSResult(int gameType, int maxFPS, int minFPS) 
        this.mGameType = gameType;
        this.mMaxFPS = maxFPS;
        this.mMinFPS = minFPS;
        if (this.mIsExistInDataBase) 
            updateAppInfo();
         else 
            insertAppInfo();
        
    

这里发生了一点小变故,可能是同样的手机,同个大编号的系统rom 存在多个小版本,并没有找到isGameInfoExist()方法。

但并不影响,咱们继续分析。看到最后,发现最终是华为系统收集app game 的周期性的fps 信息, 通过广播形式跨进程通信到其他系统app中。

在通信前,会通过cursor查询下该游戏app是否存在,也就是bulgy上的报错点。解决方案,也是基于减少cursor的查询

3.解决方案 Hook 代理

3.1 思路分析

1.通过hook 方式,代理调用EventAnalyzed类调用processAnalyze();
2.经过上面的源码分析,可知HwNsdImpl类中mEventAnalyzed属性时hook 点;
3.因IEventAnalyzed是一个抽象接口,考虑动态代理拦截其中的方法调用。

3.2 编写相应的代码

  public static void hookHwFrameworkFactory() 
        if (isSafeHookHuawei()) 
            try 
                Class<?> hwNsdImplClass = Class.forName("android.view.HwNsdImpl");
                Method getDefaultMethod = hwNsdImplClass.getDeclaredMethod("getDefault");
                getDefaultMethod.setAccessible(true);
                // 获取HwNsdImpl 类对象
                Object hwNsdImplObject = getDefaultMethod.invoke(null);
                // 调用createEventAnalyzed(),先构建出真正的EventAnalyzed对象
                Method createEventAnalyzedMethod = hwNsdImplClass.getDeclaredMethod("createEventAnalyzed");
                createEventAnalyzedMethod.setAccessible(true);
                createEventAnalyzedMethod.invoke(hwNsdImplObject);
                // 获取真正的EventAnalyzed对象
                Field mEventAnalyzedFiled = hwNsdImplClass.getDeclaredField("mEventAnalyzed");
                mEventAnalyzedFiled.setAccessible(true);
                Object oldFactory = mEventAnalyzedFiled.get(null);
                // 构建IEventAnalyzed的代理对类
                Class<?> iEventAnalyzedClass = Class.forName("com.huawei.android.hwaps.IEventAnalyzed");
                Object proxyFactory = CommonProxy.startHook(oldFactory, iEventAnalyzedClass, new ProxyCallBack() 
                    @Override
                    public Object interrupt(CommonProxy proxy, Method method, Object[] args) throws Throwable 
                        Object result = null;
                        if (method.getName().equals("processAnalyze")) 
                            //处理bugly上的问题:https://bugly.qq.com/v2/crash-reporting/crashes/1105308248/33050435/report?pid=1&crashDataType=undefined
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) 
                                //6.0到8.1的版本上屏蔽该方法的调用
                             else 
                                result = proxy.doRealInvoke(method, args);
                            
                         else 
                            result = proxy.doRealInvoke(method, args);
                        
                        return result;
                    
                );
                mEventAnalyzedFiled.set(null, proxyFactory);

             catch (Exception e) 
                e.printStackTrace();
            
        
    

3.3 云真机验证

在云真机进行验证发现,mEventAnalyzed是非静态,通过静态方式获取直接报错,可能每个Rom中代码又有新的变化:

08-29 19:39:29.633 W/System.err( 5126): java.lang.NullPointerException: null receiver
08-29 19:39:29.635 W/System.err( 5126): 	at java.lang.reflect.Field.get(Native Method)
08-29 19:39:29.635 W/System.err( 5126): 	at com.minitech.miniworld.HuaWeiHook.hookHwFrameworkFactory(HuaWeiHook.java:91)
08-29 19:39:29.635 W/System.err( 5126): 	at org.appplay.lib.GameBaseActivity$7.run(GameBaseActivity.java:625)
08-29 19:39:29.635 W/System.err( 5126): 	at android.os.Handler.handleCallback(Handler.java:761)
08-29 19:39:29.635 W/System.err( 5126): 	at android.os.Handler.dispatchMessage(Handler.java:98)
08-29 19:39:29.635 W/System.err( 处理华为Framework层中curosr和空指针问题(反编译ROM和Hook动态代理)

数据的校验和空指针

iOS 野指针和空指针

越界异常和空指针异常

C语言 野指针和空指针

C语言 野指针和空指针