修改Settings 发现的一个问题,记录一下.
代码,使用WifiManager获取Wi-Fi AP 配置:
mWifiConfig = mWifiManager.getWifiApConfiguration();
需要权限:
android.Manifest.permission.ACCESS_WIFI_STATE
android.Manifest.permission.OVERRIDE_WIFI_CONFIG
如果没有声明 OVERRIDE_WIFI_CONFIG 权限 , 则会抛出如下异常:
java.lang.SecurityException: App not allowed to read or update stored WiFi Ap config (uid = 1000)
at android.os.Parcel.readException(Parcel.java:2004)
at android.os.Parcel.readException(Parcel.java:1950)
at android.net.wifi.IWifiManager$Stub$Proxy.getWifiApConfiguration(IWifiManager.java:1769)
at android.net.wifi.WifiManager.getWifiApConfiguration(WifiManager.java:2221)
at com.android.settings.TetherSettings.initWifiTethering(TetherSettings.java:205)
at com.android.settings.TetherSettings.onCreate(TetherSettings.java:171)
如下可以看出 getWifiApConfiguration() 是一个hide 方法,并且是SystemApi ,需要权限 android.Manifest.permission.ACCESS_WIFI_STATE , 另一个权限还不能直接看出
/**
* Gets the Wi-Fi AP Configuration.
* @return AP details in WifiConfiguration
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
public WifiConfiguration getWifiApConfiguration() {
try {
return mService.getWifiApConfiguration();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
mService 是一个 IWifiManager 对象, 在 WifiManager 创建时赋值. 这个 WifiManager 是在注册系统服务时创建的,见 SystemServiceRegistry 的如下注册方法:
registerService(Context.WIFI_SERVICE, WifiManager.class,
new CachedServiceFetcher() {
@Override
public WifiManager createService(ContextImpl ctx) throws ServiceNotFoundException {
// 获取Wifi系统服务
IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_SERVICE);
// 转换为对应的接口
IWifiManager service = IWifiManager.Stub.asInterface(b);
return new WifiManager(ctx.getOuterContext(), service,
ConnectivityThread.getInstanceLooper());
}});
可以知道, b 实际上是一个binder代理对象,被代理的是系统服务 WifiService , 更确切的说应该是 WifiServiceImpl . 为什么呢?
查看 SystemServer 的 startOtherServices() 方法, 有如下代码 , 用来启动 WifiService
// Wifi Service must be started first for wifi-related services.
traceBeginAndSlog("StartWifi");
mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
traceEnd();
SystemServiceManage 的startService方法会创建 WifiService ,并调用其 onStart 方法 , 可以发现它发布的binder Service 实际上是 mImpl , 也就是 WifiServiceImpl . 因此, 通过 Context.WIFI_SERVICE 获取的实际是其代理对象 .
final WifiServiceImpl mImpl;
@Override
public void onStart() {
Log.i(TAG, "Registering " + Context.WIFI_SERVICE);
publishBinderService(Context.WIFI_SERVICE, mImpl);
}
因此 WifiManager 的 getWifiApConfiguration() 调用,实际上最终调用了WifiServiceImpl 的 getWifiApConfiguration() ,这是一个IPC 过程.
WifiServiceImpl 的 getWifiApConfiguration() 如下, 可以发现, 首先调用 enforceAccessPermission() 方法来确保有android.Manifest.permission.ACCESS_WIFI_STATE 权限, 之后调用checkConfigOverridePermission 方法来检查android.Manifest.permission.OVERRIDE_WIFI_CONFIG 权限 , 可以发现若检查没有权限,则会抛出相关的 SecurityException .
/**
* see {@link WifiManager#getWifiApConfiguration()}
* @return soft access point configuration
* @throws SecurityException if the caller does not have permission to retrieve the softap
* config
*/
@Override
public WifiConfiguration getWifiApConfiguration() {
enforceAccessPermission(); // access 权限检查
int uid = Binder.getCallingUid();
// only allow Settings UI to get the saved SoftApConfig
if (!mWifiPermissionsUtil.checkConfigOverridePermission(uid)) { // override 权限检查
// random apps should not be allowed to read the user specified config
throw new SecurityException("App not allowed to read or update stored WiFi Ap config "
+ "(uid = " + uid + ")");
}
mLog.trace("getWifiApConfiguration uid=%").c(uid).flush();
return mWifiStateMachine.syncGetWifiApConfiguration();
}
enforceAccessPermission() 方法如下, 可以看出在检查 ACCESS_WIFI_STATE 权限
private void enforceAccessPermission() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
"WifiService");
}
WifiPermissionsUtil 的 checkConfigOverridePermission 方法如下,通过WifiPermissionsWrapper.getOverrideWifiConfigPermission 方法 来检查相关权限 :
/**
* Checks if the app has the permission to override Wi-Fi network configuration or not.
*
* @param uid uid of the app.
* @return true if the app does have the permission, false otherwise.
*/
public boolean checkConfigOverridePermission(int uid) {
try {
int permission = mWifiPermissionsWrapper.getOverrideWifiConfigPermission(uid);
return (permission == PackageManager.PERMISSION_GRANTED);
} catch (RemoteException e) {
mLog.err("Error checking for permission: %").r(e.getMessage()).flush();
return false;
}
}
WifiPermissionsWrapper.getOverrideWifiConfigPermission 是真正检查 OVERRIDE_WIFI_CONFIG 权限的地方
/**
* Determines if the caller has the override wifi config permission.
*
* @param uid to check the permission for
* @return int representation of success or denied
* @throws RemoteException
*/
public int getOverrideWifiConfigPermission(int uid) throws RemoteException {
return AppGlobals.getPackageManager().checkUidPermission(
android.Manifest.permission.OVERRIDE_WIFI_CONFIG, uid);
}
查看OVERRIDE_WIFI_CONFIG 权限定义的地方( frameworks/base/core/res/AndroidManifest.xml )
这个权限是非 第三方应用 使用的 , 级别是 signature|privileged , 即有平台签名或特权应用