Android4.0 WifiP2p选项是否显示源码分析

android 4.0新增WIFI DIRECT的功能,但是在模拟器上以及一些可以升级至4.0的手机或平板,在settings里面仍然没有WIFI DIRECT功能选项。于是出于好奇,所以跟踪了一

     下源码。

   1.  查找在系统设置包中是否有WIFI DIRECT这部分代码处理。

        在Android4.0的源码路径(我的是源码路径为:myandroid_4.0)/packages/apps/Settings/src/com/android/settings/wifi中发现有P2P这个文件夹,哪说明设置包里面具有WIFI DIRECT这部分的代码处理。

    2.   查找在系统设置里对WIFI DIRECT是否有过滤。

          跟踪Settings包源码,终于发现苗头:在WirelessSettings.java (myandroid_4.0\packages\apps\settings\src\com\android\settings) 文件中

[java]
  1. @Override  
  2. public void onCreate(Bundle savedInstanceState) {  
  3.     super.onCreate(savedInstanceState);  
  4.     ..........................................  
  5.     ...........................................  
  6.     WifiP2pManager p2p = (WifiP2pManager) activity.getSystemService(Context.WIFI_P2P_SERVICE);  
  7.   
  8.     if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)) {  
  9.         getPreferenceScreen().removePreference(wifiP2p);  
  10.     } else {  
  11.         mWifiP2pEnabler = new WifiP2pEnabler(activity, wifiP2p);  
  12.     }  
  13.     ...........................................  
  14.     ...........................................  
    1)分析PackageManager.FEATURE_WIFI_DIRECT:文件PackageManager.java (frameworks\base\core\java\android\content\pm) 中:

          @SdkConstant(SdkConstantType.FEATURE)
           public static final String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";//即为传进去做判断的String。

    2)分析getPackageManager()函数

           文件SettingsPreferenceFragment.java (packages\apps\settings\src\com\android\settings)中,

[java]
  1. ...................................  
  2. ...................................  
  3.     /** 
  4.      * Returns the PackageManager from the owning Activity. 
  5.      */  
  6.     protected PackageManager getPackageManager() {  
  7.         return getActivity().getPackageManager();  
  8.     }  
  9. ...................................  
  10. ...................................  

         分析getAcivity()返回一个Activity。Activity.java (frameworks\base\core\java\android\app) 中没有 getPackageManager()函数;

         由于public class Activity extends ContextThemeWrapper,所以进入文件ContextThemeWrapper.java (frameworks\base\core\java\android\view)中,

         也没有getPackageManager()函数,同理发现public class ContextThemeWrapper extends ContextWrapper,所以进入文件ContextWrapper.java (frameworks\base\core\java\android\content) ,此时终于见到getPackageManager()踪影:

         public class ContextWrapper extends Context {
            Context mBase;

            ....................................

            ....................................

             @Override
             public PackageManager getPackageManager() {
                 return mBase.getPackageManager();
              }

            .....................................

           ......................................

           }
          继续跟踪文件Context.java (frameworks\base\core\java\android\content),发现getPackageManager()是一个抽象函数:

                   /** Return PackageManager instance to find global package information. */
                   public abstract PackageManager getPackageManager();
          分析到这里其实我也不知到该怎么继续跟踪这个函数了,想想是否跟这个文件(因为class ContextImpl extends Context)有关联ContextImpl.java (frameworks\base\core\java\android\app),的确找到了关心的代码:

[java]
  1. @Override  
  2. public PackageManager getPackageManager() {  
  3.     if (mPackageManager != null) {  
  4.         return mPackageManager;  
  5.     }  
  6.   
  7.     IPackageManager pm = ActivityThread.getPackageManager();  
  8.     if (pm != null) {  
  9.         // Doesn't matter if we make more than one instance.  
  10.         return (mPackageManager = new ApplicationPackageManager(this, pm));  
  11.     }  
  12.   
  13.     return null;  
  14. }  
          因为 final class ApplicationPackageManager extends PackageManager,所以从上面代码分析getPackageManager()返回一个ApplicationPackageManager.  

       3)  分析getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)

             由上面分析而知,getPackageManager().hasSystemFeature函数应该调到文件ApplicationPackageManager.java (frameworks\base\core\java\android\app) ,

            

[java]
  1. final class ApplicationPackageManager extends PackageManager {  
  2. ..............................  
  3. ..............................  
  4.     @Override  
  5.     public boolean hasSystemFeature(String name) {  
  6.         try {  
  7.             return mPM.hasSystemFeature(name);  
  8.         } catch (RemoteException e) {  
  9.             throw new RuntimeException("Package manager has died", e);  
  10.         }  
  11.     }  
  12. ..............................  
  13. ...............................  
  14. }  

        mPM.hasSystemFeature(name)经过AIDL实际上调用到文件PackageManagerService.java (frameworks\base\services\java\com\android\server\pm)  
[java]
  1. public class PackageManagerService extends IPackageManager.Stub {  
  2. ....................................  
  3. ....................................  
  4.     public boolean hasSystemFeature(String name) {  
  5.         synchronized (mPackages) {  
  6.             return mAvailableFeatures.containsKey(name);  
  7.         }  
  8.     }  
  9. ...................................  
  10. ...................................  
  11. }  

   mAvailableFeatures里面的内容是通过读取/system/etc/permissions下面的文档。具体代码如下所示:

[java]
  1.     void readPermissions() {  
  2.         // Read permissions from .../etc/permission directory.  
  3.         File libraryDir = new File(Environment.getRootDirectory(), "etc/permissions");  
  4.         if (!libraryDir.exists() || !libraryDir.isDirectory()) {  
  5.             Slog.w(TAG, "No directory " + libraryDir + ", skipping");  
  6.             return;  
  7.         }  
  8.         if (!libraryDir.canRead()) {  
  9.             Slog.w(TAG, "Directory " + libraryDir + " cannot be read");  
  10.             return;  
  11.         }  
  12.   
  13.         // Iterate over the files in the directory and scan .xml files  
  14.         for (File f : libraryDir.listFiles()) {  
  15.             // We'll read platform.xml last  
  16.             if (f.getPath().endsWith("etc/permissions/platform.xml")) {  
  17.                 continue;  
  18.             }  
  19.   
  20.             if (!f.getPath().endsWith(".xml")) {  
  21.                 Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");  
  22.                 continue;  
  23.             }  
  24.             if (!f.canRead()) {  
  25.                 Slog.w(TAG, "Permissions library file " + f + " cannot be read");  
  26.                 continue;  
  27.             }  
  28.   
  29.             readPermissionsFromXml(f);  
  30.         }  
  31.   
  32.         // Read permissions from .../etc/permissions/platform.xml last so it will take precedence  
  33.         final File permFile = new File(Environment.getRootDirectory(),  
  34.                 "etc/permissions/platform.xml");  
  35.         readPermissionsFromXml(permFile);  
  36.     }  
  37.   
  38.     private void readPermissionsFromXml(File permFile) {  
  39.         FileReader permReader = null;  
  40.         try {  
  41.             permReader = new FileReader(permFile);  
  42.         } catch (FileNotFoundException e) {  
  43.             Slog.w(TAG, "Couldn't find or open permissions file " + permFile);  
  44.             return;  
  45.         }  
  46.   
  47.         try {  
  48.             XmlPullParser parser = Xml.newPullParser();  
  49.             parser.setInput(permReader);  
  50.   
  51.             XmlUtils.beginDocument(parser, "permissions");  
  52.   
  53.             while (true) {  
  54.                 XmlUtils.nextElement(parser);  
  55.                 if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {  
  56.                     break;  
  57.                 }  
  58.   
  59.                 String name = parser.getName();  
  60.                 if ("group".equals(name)) {  
  61.                     String gidStr = parser.getAttributeValue(null"gid");  
  62.                     if (gidStr != null) {  
  63.                         int gid = Integer.parseInt(gidStr);  
  64.                         mGlobalGids = appendInt(mGlobalGids, gid);  
  65.                     } else {  
  66.                         Slog.w(TAG, "<group> without gid at "  
  67.                                 + parser.getPositionDescription());  
  68.                     }  
  69.   
  70.                     XmlUtils.skipCurrentTag(parser);  
  71.                     continue;  
  72.                 }   
  73. ...................  
  74. ...................  
  75. else if ("feature".equals(name)) {  
  76.                     String fname = parser.getAttributeValue(null"name");  
  77.                     if (fname == null) {  
  78.                         Slog.w(TAG, "<feature> without name at "  
  79.                                 + parser.getPositionDescription());  
  80.                     } else {  
  81.                         //Log.i(TAG, "Got feature " + fname);  
  82.                         FeatureInfo fi = new FeatureInfo();  
  83.                         fi.name = fname;  
  84.                         mAvailableFeatures.put(fname, fi);  
  85.                     }  
  86.                     XmlUtils.skipCurrentTag(parser);  
  87.                     continue;  
  88.   
  89.                 } else {  
  90.                     XmlUtils.skipCurrentTag(parser);  
  91.                     continue;  
  92.                 }  
  93.   
  94.             }  
  95.             permReader.close();  
  96.         } catch (XmlPullParserException e) {  
  97.             Slog.w(TAG, "Got execption parsing permissions.", e);  
  98.         } catch (IOException e) {  
  99.             Slog.w(TAG, "Got execption parsing permissions.", e);  
  100.         }  
  101.     }  


3.    验证上面代码分析的正确性。

        1) 启动一个ANDROID 4.0的模拟器,然后通过adb shell进入/system/etc/permissions目录下查看,
                # cd /system/etc/permissions
                # ls
                 com.android.location.provider.xml
                 platform.xml
                #
               的确没有android.hardware.wifi.direct.xml文件。

        2) 因为在源码/frameworks/base/data/etc中有android.hardware.wifi.direct.xml文件,所以我手动拷贝此文件到out/target/product/generic/system/etc/permissions/

               目录下,然后编译源码,然后用命令行指定编译完成的system.img,userdata.img,ramdisk.img来启动模拟器:

               XXX@XXX:~/Android_code/system_img$ /home/XXX/Android_install/android-sdk-linux_x86/tools/emulator  -system system.img -data userdata.img -ramdisk ramdisk.img  -partition-size 256 -avd Android4.0.3-APILevel15

               然后在模拟器设置中看到了WiFi Direct的设置项了,如图所示:



   只是点击时弹出错误框提示“Couldn't start Wi-Fi Direct”,简单的跟踪了一下是WifiP2pService.java中有WifiNative.startP2pSupplicant()的一个判断,跟到JNI层

WifiP2pService.java (frameworks\base\wifi\java\android\net\wifi\p2p)

static jboolean android_net_wifi_startP2pSupplicant(JNIEnv* env, jobject)
{
    return (jboolean)(::wifi_start_p2p_supplicant() == 0); //这个应该是硬件方面的判断。
}
   没有再继续跟踪下去了,至少关于WIFI P2P设置的过滤过程已经清楚了

 

 

android4.0.3_wifiP2P

一:android4.0.3 wifi p2p各层次的代码分布 JAVA app -> JAVA Manager -> JAVA Service -> JNI -> HAL stub

  JAVA app代码位置:

 Android sdk\samples\android-15\WiFiDirectDemo 

 

JAVA Manager代码位置:

frameworks\base\wifi\java\android\net\wifi\p2p\WifiP2pManager.java 

 

JAVA Service代码位置:

frameworks\base\wifi\java\android\net\wifi\p2p\WifiP2pService.java frameworks\base\wifi\java\android\net\wifi\WifiNative.java 

 

JNI代码位置:

Frameworks\base\core\jni\android_net_wifi_Wifi.cpp 

 

HAL stub代码位置:

hardware\libhardware_legacy\wifi\wifi.c

hardware\libhardware_legacy\include\hardware_legacy\wifi.h 

 

Setting代码位置: Packages\apps\Settings\src\com\android\settings\wifi\p2p 

JAVA Manager与JAVA Service之间的通信需要通过AIDL来实现的,即IWifiP2pManager.aidl,它用来定义接口,经过aidl工具会被编译成IWifiP2pManager.java文件,JAVA Service会去实现这些接口供Manager调用。 JAVA Service通过调用android\net\wifi目录下的WifiNative.java文件里面的native方法,从而调用到JNI层。JNI层就是去实现WifiNative.java中那些native定义的方法。 

 

二:分析代码: JAVA app主要做了以下几件

 A.enable wifi p2p,这个实际上是去调用setting的功能。

 B.扫描wifi p2p设备,并显示在一个列表中。

C.在列表中选择某个设备进行连接,连接后显示GO的信息,并且根据角色的不同做不同的 事情,GO开启一个新的线程FileServerAsyncTask用于  

// After the group negotiation, we assign the group owner as the file 

 // server. The file server is single threaded, single connection  server  socket. 而client则是 

  // The other device acts as the client. In this case, we enable the   // get file button.    这样就可以实现文件的传送了。

D.断开连接

 

 

1.我以B为例,说明层次间的调用过程  

1)文件WiFiDirectActivity.java a) JAVA app通过

intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);   

 intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);    

 intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);   

 intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);    

 receiver=newWiFiDirectBroadcastReceiver(manager,channel, this); registerReceiver(receiver, intentFilter); 注册自己关心的几类广播事件。

b) 当用户点击搜索图标后,就会调用manager.discoverPeers()

2)文件WifiP2pManager.java JAVA Manger层的discoverPeers()函数会被调用到,它通过 sendMessage(DISCOVER_PEERS, 0, c.putListener(listener)),把命令发送到JAVA  Service。

3)文件WifiP2pService.java JAVA Service层根据自己的状态机的当前状态处理JAVA Manger层发送过来的 DISCOVER_PEERS请求。

以P2pEnabledState状态为例,processMessage()处理函数根据message.what进入到 WifiP2pManager.DISCOVER_PEERS case,调用WifiNative.p2pFind()。

4)文件WifiNative.java p2pFind()会去调用native函数doBooleanCommand(),该函数在此文件中只有native的 声明,它的定义是在JNI层。

5)文件android_net_wifi_Wifi.cpp doBooleanCommand()函数在这里定义,它通过调用自己文件的doCommand()函数,再调 用::wifi_command(cmd, replybuf, &reply_len)。

6)文件wifi.c wifi_command()是HAL层的函数,它通过调用wifi_send_command(),把请求发送到 wpa_supplicant,wpa_supplicant就会与wifi driver通信。

这样顺序调用就完成了,然后一层层return回去,到3)的WifiNative.p2pFind调用处。

根据调用返回的结果判断是DISCOVER_PEERS_SUCCEEDED或者是 DISCOVER_PEERS_FAILED, 这里以成功为例。在这里JAVA Service层通过 replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED, WifiP2pManager.ERROR);

消息送回到JAVA Manger层。

7)文件WifiP2pManager.java handleMessage()函数用于接收JAVA Service层返回的消息,通过对message.what的判 断,进入到case WifiP2pManager.DISCOVER_PEERS_SUCCEEDED,然后调用 ((ActionListener) listener).onSuccess()。其中ActionListener是一个interface.

 8)文件WiFiDirectActivity.java JAVA app层有重写JAVA Manger层的interface ActionListener的方法,所以,最后调 用到的是JAVA app中的onSuccess()。

2.接收service发来的广播事件

1)文件WifiP2pService.java 前面提到JAVA app有注册自己关心的几类广播事件。 当前JAVA Service处在P2pEnabledState状态, 当它收到WifiMonitor.P2P_DEVICE_FOUND_EVENT事件后,会调用 sendP2pPeersChangedBroadcast()把消息广播出去。

 2)WiFiDirectBroadcastReceiver.java onReceive()函数会接收到注册过的广播消息。 

 3.注册回调函数 1)文件WiFiDirectBroadcastReceiver.java 前面讲到onReceive()接收到JAVA Service发来的广播,然后它通过     WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)判断 后,调用manager.requestPeers(channel, (PeerListListener)          

 activity.getFragmentManager()                        

 .findFragmentById(R.id.frag_list)),

这个函数也会一层层的最终调用到底层,该函数的第2个参数实际上是注册一个 回凋函数,因为PeerListListener是一个interface,它有个 onPeersAvailable()方法。

2)文件WifiP2pManager.java 当JAVA Manger的handleMessage()收到JAVA Service发送过来的 WifiP2pManager.RESPONSE_PEERS事件后,就会调用之前注册的回凋函数 ((PeerListListener) listener).onPeersAvailable(peers), 因为PeerListListener是interface,实际上是调用到了它的实现类的函数,即 DeviceListFragment.java文件中的onPeersAvailable() 3)文件DeviceListFragment.java onPeersAvailable()函数被调用,它负责get device list,从而让GUI显示出来

 

补充: External\wpa_supplicant\wpa_ctrl.h 提供接口给HAL层与driver通信。  frameworks\base\core\java\com\android\internal\util\StateMachine.java frameworks\base\core\java\com\android\internal\util\State.java 这两个类在JAVA Service有使用到。   在JAVA Service层中的状态机中,带WifiP2pManager的message是JAVA Manger层发送过来的,带WifiMonitor是底层发送过来的。  JAVA Service层状态机关系: Android的class StateMachine有层的概念即父状态和子状态,通过 addState(A, B)函数后,A就成为了B的子状态


你可能感兴趣的:(java,android,service,p2p,frameworks)