测试机Android版本分别有14和15
1..采用LocalOnlyHotspot进行热点创建,热点存在的生命周期和应用进程一样。此热点开启只可连接通信,不可以使用互联网。
mWifiManager.startLocalOnlyHotspot
所需权限:@RequiresPermission(allOf = {CHANGE_WIFI_STATE, ACCESS_FINE_LOCATION, NEARBY_WIFI_DEVICES},
此方法参数存在回调方法,可实现的分别是onstarted(热点创建成功,并且开启),onstopped(热点关闭),onfailed,(热点创建失败)。
通过此方法创建的热点,ssid和password都是随机的。ssid一般为Android开头
LocalOnlyHotspot存在两种开启:
1)
这一种普通应用可以直接调用,传入对应的参数即可
mWifiManager.startLocalOnlyHotspot(new WifiManager.LocalOnlyHotspotCallback() { @Override public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) { //需要创建全局对象,保持引用,不然存在几秒后开启就断开的情况。 mLocalOnlyHotspotReservation = reservation; } @Override public void onStopped() { super.onStopped(); Log.d("soft-ap-stop", "yes"); } @Override public void onFailed(int reason) { super.onFailed(reason); Log.d("soft-ap-failure", "failure" + reason); } }, new Handler(Looper.getMainLooper()));
2)这个存在config参数,意味着可以配置热点参数
此方法被标注了systemApi,通过反射调用成功了(下面有一些不需要反射哈,因为借助了ai,所以全部反射了),
// 获取 SoftApConfiguration 类 Class<?> softApConfigClass = Class.forName("android.net.wifi.SoftApConfiguration"); // 获取 Builder 类 Class<?> builderClass = Class.forName("android.net.wifi.SoftApConfiguration$Builder"); // 创建 Builder 实例 Constructor<?> builderConstructor = builderClass.getConstructor(); Object builder = builderConstructor.newInstance(); // 设置 SSID Method setSsidMethod = builderClass.getMethod("setSsid", String.class); setSsidMethod.invoke(builder, "testPot"); // 设置密码和安全类型 (WPA2_PSK) Method setPassphraseMethod = builderClass.getMethod("setPassphrase", String.class, int.class); int SECURITY_TYPE_WPA2_PSK = 1; // android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA2_PSK setPassphraseMethod.invoke(builder, "123456789", SECURITY_TYPE_WPA2_PSK); // 构建配置 Method buildMethod = builderClass.getMethod("build"); Object softApConfig = buildMethod.invoke(builder); // 获取 LocalOnlyHotspotCallback 类 Class<?> callbackClass = Class.forName("android.net.wifi.WifiManager$LocalOnlyHotspotCallback"); // 创建回调实例 Object callback = new WifiManager.LocalOnlyHotspotCallback(){ @Override public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) { mLocalOnlyHotspotReservation = reservation; } @Override public void onStopped() { super.onStopped(); Log.d("soft-ap-stop", "yes"); } @Override public void onFailed(int reason) { super.onFailed(reason); Log.d("soft-ap-failure", "yes" + reason); } }; // 获取 Handler 类 Class<?> handlerClass = Class.forName("android.os.Handler"); Constructor<?> handlerConstructor = handlerClass.getConstructor(); Object handler = handlerConstructor.newInstance(); // 调用 startLocalOnlyHotspot 方法 Method startMethod = WifiManager.class.getMethod( "startLocalOnlyHotspot", softApConfigClass, Executor.class, callbackClass ); startMethod.invoke(mWifiManager, softApConfig, ContextCompat.getMainExecutor(this),callback );
2.ConnectivityManager的startTethering
参考:Android WiFi热点_app not allowed to read or update stored wifi ap c-CSDN博客
app的build.gradle里面添加依赖
implementation ("org.mockito:mockito-core:5.6.0") implementation ("com.linkedin.dexmaker:dexmaker:2.28.3") implementation ("com.linkedin.dexmaker:dexmaker-mockito:2.28.3")
权限
<uses-permission android:name="android.permission.WRITE_SETTINGS" tools:ignore="ProtectedPermissions" />
代码:
File outputDir = getCodeCacheDir(); Class classOnStartTetheringCallback = Class.forName("android.net.ConnectivityManager$OnStartTetheringCallback"); Method startTethering = connectivityManager.getClass().getDeclaredMethod("startTethering", int.class, boolean.class, classOnStartTetheringCallback); Object proxy = ProxyBuilder.forClass(classOnStartTetheringCallback).dexCache(outputDir).handler(new InvocationHandler() { @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { switch (method.getName()) { case "onTetheringStarted": Log.d("current-status", "onTetheringStarted"); break; case "onTetheringFailed": Log.d("current-status", "onTetheringFailed"); break; default: ProxyBuilder.callSuper(o, method, objects); } return null; } }).build(); startTethering.invoke(connectivityManager, 0, false, proxy);
尝试给其加上配置会出现:App not allowed to read or update stored WiFi AP config (uid = 10025)
3.其他的尝试也存在这个问题:
java.lang.SecurityException: App not allowed to read or update stored WiFi AP config (uid = 10025)
了解到可能是权限不足,不是系统级别,如果有人解决了,麻烦踢一下我,谢谢
其他的尝试(tetheringManager.startTethering)
Class<?> tetheringManagerClass = Class.forName("android.net.TetheringManager"); // 2. 获取 TetheringManager 实例 Object tetheringManager = getSystemService("tethering"); // 3. 获取 TetheringRequest 类及其 Builder 内部类 Class<?> tetheringRequestClass = Class.forName("android.net.TetheringManager$TetheringRequest"); Class<?> builderClass = Class.forName("android.net.TetheringManager$TetheringRequest$Builder"); // 4. 创建 Builder 实例(假设网络类型为 WIFI=1) Constructor<?> builderConstructor = builderClass.getConstructor(int.class); Object builder = builderConstructor.newInstance(1); // TETHERING_WIFI=1 // 5. 通过 Builder 设置参数(示例:豁免权限检查) Method setExemptMethod = builderClass.getMethod("setExemptFromEntitlementCheck", boolean.class); setExemptMethod.invoke(builder, true); // 6. 构建 TetheringRequest 实例 Method buildMethod = builderClass.getMethod("build"); Object tetheringRequest = buildMethod.invoke(builder); Class<?> callbackClass = Class.forName("android.net.TetheringManager$StartTetheringCallback"); // // 使用 ProxyBuilder 创建代理对象 InvocationHandler handler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { switch (method.getName()) { case "onTetheringStarted": Log.d("TAG", "热点已启动"); break; case "onTetheringFailed": Log.e("TAG", "启动失败,错误码: " + args[0]); break; default: break; } return null; // 对于接口方法,通常返回 null 或适当的默认值 } }; File outputDir = getCodeCacheDir(); Object proxy = ProxyBuilder.forClass(Object.class) // 使用 Object 类作为基类 .dexCache(outputDir) .implementing(callbackClass) // 显式声明实现的接口 .handler(handler) .build(); // 8. 获取 startTethering 方法并调用 Method startTetheringMethod = tetheringManagerClass.getDeclaredMethod( "startTethering", tetheringRequestClass, Executor.class, callbackClass ); startTetheringMethod.setAccessible(true); // 9. 执行调用 startTetheringMethod.invoke( tetheringManager, tetheringRequest, ContextCompat.getMainExecutor(this), proxy );