在机顶盒ROM研发过程中,有时为了完成某一需求,需要在framework中添加系统接口,所以本篇文章就简单介绍一下在framework中添加系统接口的方法。在framework添加接口大致可分为两类:在原生Service中添加和在定制的Service中添加,本篇文章基于Android4.4.2系统,进行简单介绍。 以Amlogic905代码为例,机顶盒所连接电视机相关信息存储在节点”/sys/class/amhdmitx/amhdmitx0/edid“中,使用cat /sys/class/amhdmitx/amhdmitx0/edid命令,查看得该节点内容如下: 由上述内容可知,我们需要的就是通过Physcial size(cm): 52 x 29(即屏幕的宽高),来计算出屏幕的对角线尺寸。具体计算公式为: framework层供外部调用的接口都是由Binder通信来实现的,简单关系如下: 接下来就以实现”获取HDMI线连接的电视机尺寸功能“为例,进行简单介绍。由于电视机尺寸属于显示类功能范畴,所以在frameworks/base/core/java/android/os/display/DisplayManager.java中增加该接口。 然后,在frameworks/base/services/java/com/android/server/DisplayService.java添加getTVSize接口的功能实现,如下: 最后,在frameworks/base/core/java/android/os/display/DisplayManager.java中,添加对getTVSize功能的调用,如下: 此时,就可以在上层应用中调用该接口,示例如下: 本章节简单介绍怎么在framework中添加自己定制的Service。实现了定制的Service,添加一个接口也就变得非常简单。假设对该Service的要求是: 要添加定制Service的第一步,就是在frameworks/base/services/java/com/android/server/SystemServer.java的initAndLoop方法中,将DevInfoManagerService添加到ServiceManager中,具体如下: 接下来要做的就是在创建一个aidl文件,定义对应接口。由于有包名路径限制,所以需要创建一个frameworks/base/core/java/android/app/IDevInfoManager.aidl的文件,内容如下: 此时应该会有人有这样的疑问:规定的getValue只有一个参数,此处为什么有两个?答案就是第二个参数是用来包名校验的,之所以与规定的接口参数个数不一致,是因为这个接口是framework层的实现接口,而不是供上层调用的接口(稍后介绍)。 此时要定义一个供上层调用的接口,所以需要创建一个frameworks/base/core/java/android/app/DevInfoManager.java的接口文件,内容如下: 该接口内容不难理解,与规定的接口参数、接口名都保持一致。 此时问题就来了,IDevInfoManager.aidl定义的方法为getValue(String name, String packageName),而DevInfoManager.java中定义的方法为getValue(String name),两者不一致,各位童鞋想一下此时要怎么办? 该文件中出现了mPackageName参数,但是怎么获取这个参数呢?此时就不得不提Android系统中另一个非常重要的类:ContextImpl.java。 上面介绍了接口应该怎么定义与调用,现在要做的就是实现具体的接口,即创建一个frameworks/base/services/java/com/android/server/DevInfoManagerService.java,要实现的内容如下: 该类的内容较容易理解,可能有个方法需要解释一下:即systemReady方法,此方法并没有被调用,看上去”毫无作用”。其实,此方法确实未用上,写此方法的意义在于模仿其他系统Service。有些系统Service中的这个systemReady方法在SystemServer.java的initAndLoop方法中有被调用,用来处理系统启动完毕消息。 在上面几个文件中介绍了如何在framework中添加一个Service,但是上层应用还是调用不了这个接口的,原因是什么呢?因为这一系列接口还没编译到最终的版本里。所以还需要在frameworks/base/Android.mk中添加IDevInfoManager.aidl,具体内容如下: 经过以上对接口的添加,上层应用就可以调用getValue方法了,具体调用方式如下: 至此,添加系统接口的方法已介绍完毕。一、知识准备
1.1、电视机屏幕宽高的获取
root@p201_iptv:/ # cat /sys/class/amhdmitx/amhdmitx0/edid Rx Brand Name: PHL Rx Product Name: PHILIPS Physcial size(cm): 52 x 29 Manufacture Week: 1 Manufacture Year: 2015 EDID Verison: 1.3 EDID block number: 0x1 blk0 chksum: 0x2c Source Physical Address[a.b.c.d]: 1.0.0.0 native Mode f1, VIC (native 16): ColorDeepSupport 2 31 16 20 5 19 4 2 3 32 22 18 6 7 1 Audio {format, channel, freq, cce} {1, 1, 7, 7} {10, 7, 6, 0} Speaker Allocation: 1 Vendor: 0xc03 MaxTMDSClock1 290 MHz SCDC: 0 RR_Cap: 0 LTE_340M_Scramble: 0 checkvalue: 0x2cc80000
1.2、电视机屏幕尺寸的计算
(int)(Math.sqrt(height*height +weight*weight)/2.54 +0.5)
1.3、Binder通信
二、在原生Service中添加
由1.3章节内容可知,要在framework添加一个接口,至少需要修改三个地方:
1>定义接口的地方,一般为IXXXService.aidl文件。
2>实现接口的地方,一般为XXXService.java文件。
3>调用接口的地方,一般为XXXManager.java文件。
接下来,就进入真正的代码修改阶段。首先,在frameworks/base/core/java/android/os/display/IDisplayService.aidl中添加getTVSize接口,如下: String getTVSize();
import java.lang.Math.*; //add TV info path private static final String TV_INFO_PATH = "/sys/class/amhdmitx/amhdmitx0/edid"; //get TV width and height public String getTVHeightAndWeight(){ return getNodeValue("Physcial size(cm)",TV_INFO_PATH); } //get TV diagonal size public String getTVSize(){ String heightAndWeight=getTVHeightAndWeight(); String[] sizes=heightAndWeight.split("x"); int height=Integer.valueOf(sizes[0].toString().trim()); int weight=Integer.valueOf(sizes[1].toString().trim()); int size=(int)(Math.sqrt(height*height +weight*weight)/2.54 +0.5); Log.d(TAG,"TV size:"+size); return String.valueOf(size); } //get node value public String getNodeValue(String item,String path){ if (!new File(path).exists()) { Slog.e(TAG, "File not found: " + path); return ""; } String str = null; StringBuilder nodeValues = new StringBuilder(); FileReader fr = null; BufferedReader br = null; try { fr = new FileReader(path); br = new BufferedReader(fr); try { while ((str = br.readLine()) != null) { if(str != null) nodeValues.append(str); nodeValues.append('n'); }; fr.close(); br.close(); } catch (IOException e) { e.printStackTrace(); } } catch (FileNotFoundException e) { e.printStackTrace(); }finally { try { if(fr != null) fr.close(); if(br != null) br.close(); }catch (IOException e) { e.printStackTrace(); } } String[] allValues = nodeValues.toString().split("n"); for(int i=0;i<allValues.length;i++){ if(allValues[i].contains(item)){ String[] itemArr=allValues[i].split(":"); return itemArr[1].trim(); } } return ""; }
public String getTVSize(){ try{ Slog.i(TAG,"getTVSize"); String tVSize = mService.getTVSize(); return tVSize; }catch(RemoteException ex){ Slog.e(TAG,"getTVSize error!"); return ""; } }
import android.os.display.DisplayManager; DisplayManager mDisplayManager = (DisplayManager)m_Context.getSystemService(Context.DISPLAY_MANAGER_SERVICE); String tvSize = mDisplayManager.getTVSize();
三、实现定制的Service
1>DevInfoManager.java的完整路径为android.app.DevInfoManager,做为一个系统服务提供给上层应用使用,即:
包名: android.app
类名: DevInfoManager
2>获取实例的方式为:
DevInfoManager mDevInfoManager = (DevInfoManager) getSystemService(DevInfoManager.DATA_SERVER);
3>需要实现的接口是:
String getValue(String name),特殊的要求是上层应用在使用此接口查询参数时,需要进行权限校验,也就是只在白名单中的apk才能通过该接口查询到参数。
要添加一个全新的Service,就得从SystemServer说起。至于为什么要说SystemServer的原因,先看一张图:
上图就是简版的Android启动流程,Android系统中所需要的Service都是在SystemServer中启动的。接下来就分步介绍该接口的实现步骤。3.1、SystemServer.java
DevInfoManagerService devInfoManager = null; try { Slog.i(TAG, "DevInfo Manager Service"); devInfoManager = new DevInfoManagerService(context); ServiceManager.addService(Context.DATA_SERVICE, devInfoManager); } catch (Throwable e) { reportWtf("starting DevInfoManagerService", e); }
3.2、IDevInfoManager.aidl
package android.app; interface IDevInfoManager { String getValue(String name, String packageName); }
3.3、DevInfoManager.java
package android.app; public interface DevInfoManager { public String getValue(String name); }
3.4、DevInfoManagerImpl.java&ContextImpl.java
解决的方法是创建一个中间桥梁文件,连接两者,即frameworks/base/core/java/android/app/DevInfoManagerImpl.java,该文件的内容如下:package android.app; import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; import android.os.Handler; import android.util.Log; public class DevInfoManagerImpl implements DevInfoManager { private static final String TAG = "DevInfoManagerImpl"; private IDevInfoManager mService; private String mPackageName; public DevInfoManagerImpl(IDevInfoManager service, String packageName){ mService = service; mPackageName = packageName; } @Override public String getValue(String name) { Log.d(TAG, "enter getValue, name=" + name); if(mService != null){ try { return mService.getValue(name,mPackageName); } catch(RemoteException e){ Log.e(TAG, "getValue, RemoteException, " + e); } } return null; } }
从前面的例子能够看出,上层应用在调用系统接口时,需要用一个Context类型的对象,调用该对象的getSystemService方法获取系统Service的代理对象。同时,在获取系统Service代理对象时,需要现在ServiceManager中查询是否存在这样一个Service,要想查询到Service,就肯定要先注册Service,注册的地方有两个:一个是前面已经看到的SystemServer.java中添加,另一个就是在ContextImpl.java添加。为什么是在ContextImpl.java中添加呢?因为ContextImpl恰恰是Context的实现类。此时,需要在frameworks/base/core/java/android/app/ContextImpl.java中的static块中,添加的内容如下: registerService(DATA_SERVER, new ServiceFetcher() { public Object createService(ContextImpl ctx) { if(DEBUG)Log.d(TAG, "register DATA_SERVICE getPackageName " + ctx.getPackageName()); IBinder b = ServiceManager.getService(DATA_SERVICE); IDevInfoManager service = IDevInfoManager.Stub.asInterface(b); return new DevInfoManagerImpl(service, ctx.getPackageName()); } });
3.5、DevInfoManagerService.java
package com.android.server; import java.io.File; import java.io.File; import java.util.Map; import android.util.Log; import android.text.TextUtils; import android.content.Context; import android.content.SharedPreferences; import android.os.RemoteException; import android.os.ServiceManager; import android.os.RemoteException; import android.app.DevInfoManager; import android.app.IDevInfoManager; class DevInfoManagerService extends IDevInfoManager.Stub { private static final String TAG = "DevInfoManagerService"; private Context mContext; final Object mLock = new Object(); String readOnlyPermApkNameList[] = { "com.jscmcc.tvstore", "com.certus.ottstb.qosmonloader", "com.jscmcc.hdcservice", "com.huawei.ma", "com.android.settings", "com.ysten.settings", "com.jscmcc.hdcmessagewidget" }; public DevInfoManagerService(Context context){ this.mContext = context; } public void systemReady(){ synchronized(mLock){ Log.d(TAG, "enter systemReady"); } } public String getValue(String name, String packageName) throws RemoteException { String value = null; synchronized(mLock){ Log.d(TAG, "enter getValue , butai name=" + name); if((!(checkReadOnlyPerm(packageName))) { Log.d(TAG,"getValue permition denied"); value = ""; return value; } if(name.equals("launcher")){ value = getPropValue("account_password"); } Log.d(TAG, "exit getValue, value = " + value); return value; } } public String getPropValue(String key){ String value = null; Log.d(TAG, "getPropValue mPropFile=" + mPropFile); value = readFromFile(mPropFile, key); Log.d(TAG, "getPropValue key=" + key + ",value=" + value); return value; } private String readFromFile(String fileName, String key){ String result = null; result = readSharedPref(mContext, key); Map<String, String> map = getFileMap(fileName); if(TextUtils.isEmpty(result)){ if(map != null && map.containsKey(key)){ result = map.get(key); } } else { if(map != null && map.containsKey(key)){ map.put(key, result); } } return result; } private String readSharedPref(Context context, String key){ String result = null; SharedPreferences sp = context.getSharedPreferences(SHARED_PREF, Context.MODE_WORLD_WRITEABLE); result = sp.getString(key, null); File f = context.getSharedPrefsFile(SHARED_PREF); Log.d(TAG,"----readSharedPref---result="+result+"--1--f="+f.getAbsolutePath().toString()); return result; } private boolean checkReadOnlyPerm (String packageName) { for(int i=0;i<readOnlyPermApkNameList.length;i++){ String a = readOnlyPermApkNameList[i]; if(packageName.equals(a)){ Log.d(TAG,"check Perm read only true "+packageName); return true; } } Log.d(TAG,"check Perm read only false "+packageName); return false; } }
3.6、Android.mk
LOCAL_SRC_FILES += core/java/android/app/IDevInfoManager.aidl
3.7、上层应用调用接口示例
import android.app.DevInfoManager; DevInfoManager manager = (DevInfoManager) mContext.getSystemService(DevInfoManager.DATA_SERVER); String packageName = manager.getValue("launcher");
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算