前言: WiFi熱點設置頁面的安全性選項在Android 4.x上有「無」、「WPA PSK」、「WPA2 PSK」三個選項,在Android 5.0(含)以後去掉了WPA PSK選項(部分手機廠家會修改ROM,有些手機4.4就沒有這個選項了,安全性選項下拉選項是在packages/apps/Settings/res/values/arrays.xml這個文件的wifi_ap_security數組中定義的),當安全性爲「無」時鏈接熱點不須要密碼,其餘兩種都是要輸入密碼才能鏈接的。本文將講解用代碼自動建立、跳轉系統熱點設置頁手動建立兩種方式建立熱點,以及當targetSdkVersion設爲23以上時如何處理權限問題。java
熱點的安全性選項對應的Java類是在WifiConfiguration.java中定義的(源碼路徑:/frameworks/base/wifi/java/android/net/wifi/WifiConfiguration.java,API 25,不一樣的API內容有差別)android
1 public static class KeyMgmt { 2 private KeyMgmt() { } 3 4 /** WPA is not used; plaintext or static WEP could be used. */ 5 public static final int NONE = 0; 6 /** WPA pre-shared key (requires {@code preSharedKey} to be specified). */ 7 public static final int WPA_PSK = 1; 8 /** WPA using EAP authentication. Generally used with an external authentication server. */ 9 public static final int WPA_EAP = 2; 10 /** IEEE 802.1X using EAP authentication and (optionally) dynamically 11 * generated WEP keys. */ 12 public static final int IEEE8021X = 3; 13 14 /** WPA2 pre-shared key for use with soft access point 15 * (requires {@code preSharedKey} to be specified). 16 * @hide 17 */ 18 @SystemApi 19 public static final int WPA2_PSK = 4; 20 /** 21 * Hotspot 2.0 r2 OSEN: 22 * @hide 23 */ 24 public static final int OSEN = 5; 25 26 public static final String varName = "key_mgmt"; 27 28 public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP", "IEEE8021X", 29 "WPA2_PSK", "OSEN" }; 30 }
能夠看到,無、WPA PSK、WPA2 PSK這三個選項分別對應到KeyMgmt.NONE、KeyMgmt.WPA_PSK、KeyMgmt.WPA2_PSK,最後一個WPA2_PSK添加了@SystemApi標記,咱們不能夠直接訪問的,怎麼建立這三種形式的熱點呢?git
添加須要的權限github
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
Android並無公開建立WiFi熱點的API,因此咱們只能經過反射的方式實現,建立安全性爲「無」的代碼以下:shell
1 /** 2 * 自定義wifi熱點 3 * 4 * @param enabled 開啓or關閉 5 * @return 6 */ 7 private boolean setWifiApEnabled(boolean enabled) { 8 boolean result = false; 9 WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); 10 if (enabled) { 11 //wifi和熱點不能同時打開,因此打開熱點的時候須要關閉wifi 12 if (wifiManager.isWifiEnabled()) { 13 wifiManager.setWifiEnabled(false); 14 } 15 } 16 try { 17 //熱點的配置類 18 WifiConfiguration apConfig = new WifiConfiguration(); 19 //配置熱點的名稱 20 apConfig.SSID = "ap_test"; 21 //配置熱點的密碼,至少八位 22 apConfig.preSharedKey = ""; 23 //配置熱點安全性選項 24 apConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); 25 //經過反射調用設置熱點 26 Method method = wifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class); 27 //返回熱點打開狀態 28 result = (Boolean) method.invoke(wifiManager, apConfig, enabled); 29 if (!result) { 30 Toast.makeText(this, "熱點建立失敗,請手動建立!", Toast.LENGTH_SHORT).show(); 31 openAPUI(); 32 } 33 } catch (Exception e) { 34 Toast.makeText(this, "熱點建立失敗,請手動建立!", Toast.LENGTH_SHORT).show(); 35 openAPUI(); 36 } 37 return result; 38 }
建立安全性爲「WPA PSK」的熱點須要修改2二、24行,須要注意的是:建立熱點後,在不支持WPA_PSK的手機上(Android 4.3以上版本)打開系統熱點頁面,「安全性」將顯示爲「無」,看不到密碼文本框,可是鏈接此類熱點仍是須要輸入密碼的。數組
1 //配置熱點的密碼,至少八位 2 apConfig.preSharedKey = "12345678"; 3 //配置熱點安全性 4 apConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
建立安全性選項爲WPA2 PSK只需將上面代碼中第4行改成安全
apConfig.allowedKeyManagement.set(4);
直接將值指定爲4,緣由上面講過,這裏還有另一個坑:在MIUI系統上,WPA2 PSK這個值爲6,若是在MIUI上運行須要設爲6才能夠,咱們能夠判斷一下手機ROM的版本,根據不一樣的ROM設爲4或6。(後面有另一種判斷方式)網絡
1 /** 2 * 判斷是否爲MIUI系統,參考http://blog.csdn.net/xx326664162/article/details/52438706 3 * 4 * @return 5 */ 6 public static boolean isMIUI() { 7 try { 8 String KEY_MIUI_VERSION_CODE = "ro.miui.ui.version.code"; 9 String KEY_MIUI_VERSION_NAME = "ro.miui.ui.version.name"; 10 String KEY_MIUI_INTERNAL_STORAGE = "ro.miui.internal.storage"; 11 Properties prop = new Properties(); 12 prop.load(new FileInputStream(new File(Environment.getRootDirectory(), "build.prop"))); 13 14 return prop.getProperty(KEY_MIUI_VERSION_CODE, null) != null 15 || prop.getProperty(KEY_MIUI_VERSION_NAME, null) != null 16 || prop.getProperty(KEY_MIUI_INTERNAL_STORAGE, null) != null; 17 } catch (IOException e) { 18 return false; 19 } 20 }
apConfig.allowedKeyManagement.set(isMIUI() ? 6 : 4);
至於MIUI爲何設爲6就無從得知了,我嘗試在MIUI論壇發帖詢問緣由,帖子一直沒有審覈經過,因而可知使用系統非公開API是有必定風險的。app
20170914更新ide
通過研究,能夠從WifiConfiguration.KeyMgmt.strings這個數組中讀取全部的安全性選項(MIUI的安全性選項爲NONE, WPA_PSK, WPA_EAP, IEEE8021X, WAPI_PSK, WAPI_CERT, WPA2_PSK, OSEN,多了WAPI_PSK, WAPI_CERT這兩個),咱們只要從中找到WPA2_PSK的索引值就能夠了,不須要根據ROM類型判斷,因此上述代碼能夠修改成
1 int indexOfWPA2_PSK = 4; 2 //從WifiConfiguration.KeyMgmt數組中查找WPA2_PSK的值 3 for (int i = 0; i < WifiConfiguration.KeyMgmt.strings.length; i++) { 4 if (WifiConfiguration.KeyMgmt.strings[i].equals("WPA2_PSK")) { 5 indexOfWPA2_PSK = i; 6 break; 7 } 8 } 9 apConfig.allowedKeyManagement.set(indexOfWPA2_PSK);
若是自動建立熱點失敗,咱們也能夠跳轉系統設置頁讓用戶手動建立。 Android一樣沒有提供跳轉熱點設置頁面的Intent,咱們能夠打開熱點設置頁面後,經過使用adb shell actives去查找相應的APP包名、類,打開系統熱點設置頁的代碼以下:
/** * 打開網絡共享與熱點設置頁面 */ private void openAPUI() { Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //打開網絡共享與熱點設置頁面 ComponentName comp = new ComponentName("com.android.settings", "com.android.settings.Settings$TetherSettingsActivity"); intent.setComponent(comp); startActivity(intent); }
直接貼代碼吧,判斷熱點是否打開能夠參考源碼中的isWifiApEnabled()方法
1 /** 2 * 讀取熱點配置信息 3 */ 4 private void getWiFiAPConfig() { 5 try { 6 WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); 7 Method method = wifiManager.getClass().getMethod("getWifiApConfiguration"); 8 WifiConfiguration apConfig = (WifiConfiguration) method.invoke(wifiManager); 9 if (apConfig == null) { 10 tvInfo.setText("未配置熱點"); 11 return; 12 } 13 tvInfo.setText(String.format("熱點名稱:%s\r\n", apConfig.SSID)); 14 tvInfo.append(String.format("密碼:%s\n", apConfig.preSharedKey)); 15 //使用apConfig.allowedKeyManagement.toString()返回{0}這樣的格式,須要截取中間的具體數值 16 //下面幾種寫法均可以 17 //int index = Integer.valueOf(apConfig.allowedKeyManagement.toString().substring(1, 2)); 18 //int index = Integer.valueOf(String.valueOf(apConfig.allowedKeyManagement.toString().charAt(1))); 19 //int index = Integer.valueOf(apConfig.allowedKeyManagement.toString().charAt(1)+""); 20 int index = apConfig.allowedKeyManagement.toString().charAt(1) - '0'; 21 //從KeyMgmt數組中取出對應的文本 22 String apType = WifiConfiguration.KeyMgmt.strings[index]; 23 tvInfo.append(String.format(Locale.getDefault(), "WifiConfiguration.KeyMgmt:%s\r\n", Arrays.toString(WifiConfiguration.KeyMgmt.strings))); 24 tvInfo.append(String.format(Locale.getDefault(), "安全性:%d,%s\r\n", index, apType)); 25 isOpen = isWifiApEnabled(); 26 tvInfo.append("是否已開啓:" + isOpen); 27 } catch (NoSuchMethodException e) { 28 e.printStackTrace(); 29 } catch (IllegalAccessException e) { 30 e.printStackTrace(); 31 } catch (InvocationTargetException e) { 32 e.printStackTrace(); 33 } 34 }
系統會自動建立一個默認的熱點,若是咱們不須要自定義熱點參數的話,能夠直接讀取系統的熱點參數,請參考源碼中的switchWifiApEnabled(boolean enabled)方法
若是APP的targetSdkVersion爲23以上,須要在清單文件中添加android.permission.WRITE_SETTINGS權限,並在運行時申請,跳轉系統設置頁由用戶開啓。
1 private boolean isHasPermissions() { 2 boolean result = false; 3 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 4 if (!Settings.System.canWrite(this)) { 5 Toast.makeText(this, "打開熱點須要啓用「修改系統設置」權限,請手動開啓", Toast.LENGTH_SHORT).show(); 6 7 //清單文件中須要android.permission.WRITE_SETTINGS,不然打開的設置頁面開關是灰色的 8 Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS); 9 intent.setData(Uri.parse("package:" + this.getPackageName())); 10 //判斷系統可否處理,部分ROM無此action,如魅族Flyme 11 if (intent.resolveActivity(getPackageManager()) != null) { 12 startActivity(intent); 13 } else { 14 //打開應用詳情頁 15 intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); 16 intent.setData(Uri.parse("package:" + this.getPackageName())); 17 if (intent.resolveActivity(getPackageManager()) != null) { 18 startActivity(intent); 19 } 20 } 21 } else { 22 result = true; 23 } 24 } else { 25 result = true; 26 } 27 return result; 28 }
項目源碼:https://github.com/fly263/APDemo
PS:佈局文件使用了ConstraintLayout,若是沒有安裝,須要在Android Studio中點擊File-Settings-System Settings-Android SDK,切換到SDK Tools標籤頁,下載ConstraintLayout for Android、Solver for ConstraintLayout兩個支持庫