概述
随着Android的更新,越新的版本收紧的权限越来越多,伴随着很多曾经可用的接口慢慢地出现了问题。
比如: TelephonyManager.getSubscriberId()
public static void testGetSubId(Context ctx){
//Android 11:
// java.lang.SecurityException: getSubscriberId: The user 10125 does not meet the requirements to access device identifiers.
TelephonyManager telMgr = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE);
@SuppressLint("MissingPermission")
String subId = telMgr.getSubscriberId();
android.util.Log.d(TAG, "testGetSubId subId=" + subId);
}
原因
在代码注释中已经写得很明白:
The user 10125 does not meet the requirements to access device identifiers
要做的事情很简单, 找到权限检查的地方, 去掉它
权限检查的位置
frameworks/base/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
public static boolean checkCallingOrSelfReadDeviceIdentifiers(Context context, int subId,
String callingPackage, @Nullable String callingFeatureId, String message) {
return true || checkPrivilegedReadPermissionOrCarrierPrivilegePermission(
context, subId, callingPackage, callingFeatureId, message, true);
}
public static boolean checkCallingOrSelfReadSubscriberIdentifiers(Context context, int subId,
String callingPackage, @Nullable String callingFeatureId, String message) {
return true || checkPrivilegedReadPermissionOrCarrierPrivilegePermission(
context, subId, callingPackage, callingFeatureId, message, false);
}
private static boolean checkPrivilegedReadPermissionOrCarrierPrivilegePermission(
Context context, int subId, String callingPackage, @Nullable String callingFeatureId,
String message, boolean allowCarrierPrivilegeOnAnySub) {
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
// If the calling package has carrier privileges for specified sub, then allow access.
if (checkCarrierPrivilegeForSubId(context, subId)) return true;
// If the calling package has carrier privileges for any subscription
// and allowCarrierPrivilegeOnAnySub is set true, then allow access.
if (allowCarrierPrivilegeOnAnySub && checkCarrierPrivilegeForAnySubId(context, uid)) {
return true;
}
PermissionManager permissionManager = (PermissionManager) context.getSystemService(
Context.PERMISSION_SERVICE);
if (permissionManager.checkDeviceIdentifierAccess(callingPackage, message, callingFeatureId,
pid, uid) == PackageManager.PERMISSION_GRANTED) {
return true;
}
return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage,
message);
}
/**
* Reports a failure when the app with the given pid/uid cannot access the requested identifier.
*
* @returns false if the caller is targeting pre-Q and does have the READ_PHONE_STATE
* permission or carrier privileges.
* @throws SecurityException if the caller does not meet any of the requirements for the
* requested identifier and is targeting Q or is targeting pre-Q
* and does not have the READ_PHONE_STATE permission or carrier
* privileges.
*/
private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid,
int uid, String callingPackage, String message) {
ApplicationInfo callingPackageInfo = null;
try {
callingPackageInfo = context.getPackageManager().getApplicationInfoAsUser(
callingPackage, 0, UserHandle.getUserHandleForUid(uid));
} catch (PackageManager.NameNotFoundException e) {
// If the application info for the calling package could not be found then assume the
// calling app is a non-preinstalled app to detect any issues with the check
Log.e(LOG_TAG, "Exception caught obtaining package info for package " + callingPackage,
e);
}
// The current package should only be reported in StatsLog if it has not previously been
// reported for the currently invoked device identifier method.
boolean packageReported = sReportedDeviceIDPackages.containsKey(callingPackage);
if (!packageReported || !sReportedDeviceIDPackages.get(callingPackage).contains(
message)) {
Set invokedMethods;
if (!packageReported) {
invokedMethods = new HashSet<String>();
sReportedDeviceIDPackages.put(callingPackage, invokedMethods);
} else {
invokedMethods = sReportedDeviceIDPackages.get(callingPackage);
}
invokedMethods.add(message);
TelephonyCommonStatsLog.write(TelephonyCommonStatsLog.DEVICE_IDENTIFIER_ACCESS_DENIED,
callingPackage, message, /* isPreinstalled= */ false, false);
}
Log.w(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message + ":"
+ subId);
// if the target SDK is pre-Q then check if the calling package would have previously
// had access to device identifiers.
if (callingPackageInfo != null && (
callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q)) {
if (context.checkPermission(
android.Manifest.permission.READ_PHONE_STATE,
pid,
uid) == PackageManager.PERMISSION_GRANTED) {
return false;
}
if (checkCarrierPrivilegeForSubId(context, subId)) {
return false;
}
}
throw new SecurityException(message + ": The user " + uid
+ " does not meet the requirements to access device identifiers.");
}
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@Override
public int checkDeviceIdentifierAccess(@Nullable String packageName, @Nullable String message,
@Nullable String callingFeatureId, int pid, int uid) {
// If the check is being requested by an app then only allow the app to query its own
// access status.
int callingUid = mInjector.getCallingUid();
int callingPid = mInjector.getCallingPid();
if (UserHandle.getAppId(callingUid) >= Process.FIRST_APPLICATION_UID && (callingUid != uid
|| callingPid != pid)) {
String response = String.format(
"Calling uid %d, pid %d cannot check device identifier access for package %s "
+ "(uid=%d, pid=%d)",
callingUid, callingPid, packageName, uid, pid);
Log.w(TAG, response);
throw new SecurityException(response);
}
// Allow system and root access to the device identifiers.
final int appId = UserHandle.getAppId(uid);
if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID) {
return PackageManager.PERMISSION_GRANTED;
}
// Allow access to packages that have the READ_PRIVILEGED_PHONE_STATE permission.
if (mInjector.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid,
uid) == PackageManager.PERMISSION_GRANTED) {
return PackageManager.PERMISSION_GRANTED;
}
// If the calling package is not null then perform the appop and device / profile owner
// check.
if (packageName != null) {
// Allow access to a package that has been granted the READ_DEVICE_IDENTIFIERS appop.
long token = mInjector.clearCallingIdentity();
AppOpsManager appOpsManager = (AppOpsManager) mInjector.getSystemService(
Context.APP_OPS_SERVICE);
try {
if (appOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_READ_DEVICE_IDENTIFIERS, uid,
packageName, callingFeatureId, message) == AppOpsManager.MODE_ALLOWED) {
return PackageManager.PERMISSION_GRANTED;
}
} finally {
mInjector.restoreCallingIdentity(token);
}
// Check if the calling packages meets the device / profile owner requirements for
// identifier access.
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) mInjector.getSystemService(Context.DEVICE_POLICY_SERVICE);
if (devicePolicyManager != null && devicePolicyManager.hasDeviceIdentifierAccess(
packageName, pid, uid)) {
return PackageManager.PERMISSION_GRANTED;
}
}
return PackageManager.PERMISSION_DENIED;
}
WifiManager.setWifiEnabled
从官方文档中已经说明: SDK >= Q 这个接口会直接返回 false,不再生效
示例
build.gradle 注意targetSdkVersion
defaultConfig {
applicationId "com.xxx.apitester"
minSdkVersion 21
targetSdkVersion 30
//...
}
WifiManager wifiMgr = (WifiManager) ctx.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
boolean en = wifiMgr.isWifiEnabled();
//返回false
boolean res = wifiMgr.setWifiEnabled(!en);
如果调用了从LOG中可以找到: setWifiEnabled not allowed for uid=xxx
原因
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java
/**
* see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
* @param enable {@code true} to enable, {@code false} to disable.
* @return {@code true} if the enable/disable operation was
* started or is already in the queue.
*/
@Override
public synchronized boolean setWifiEnabled(String packageName, boolean enable) {
if (enforceChangePermission(packageName) != MODE_ALLOWED) {
return false;
}
/*********************************/
//检查APP的SDK版本等信息。
/*********************************/
boolean isPrivileged = isPrivileged(Binder.getCallingPid(), Binder.getCallingUid());
if (!isPrivileged && !isDeviceOrProfileOwner(Binder.getCallingUid(), packageName)
&& !mWifiPermissionsUtil.isTargetSdkLessThan(packageName, Build.VERSION_CODES.Q,
Binder.getCallingUid())
&& !isSystem(packageName, Binder.getCallingUid())) {
mLog.info("setWifiEnabled not allowed for uid=%")
.c(Binder.getCallingUid()).flush();
return false;
}
// If Airplane mode is enabled, only privileged apps are allowed to toggle Wifi
if (mSettingsStore.isAirplaneModeOn() && !isPrivileged) {
mLog.err("setWifiEnabled in Airplane mode: only Settings can toggle wifi").flush();
return false;
}
// If SoftAp is enabled, only privileged apps are allowed to toggle wifi
if (!isPrivileged && mTetheredSoftApTracker.getState() == WIFI_AP_STATE_ENABLED) {
mLog.err("setWifiEnabled with SoftAp enabled: only Settings can toggle wifi").flush();
return false;
}
mLog.info("setWifiEnabled package=% uid=% enable=%").c(packageName)
.c(Binder.getCallingUid()).c(enable).flush();
long ident = Binder.clearCallingIdentity();
try {
if (!mSettingsStore.handleWifiToggled(enable)) {
// Nothing to do if wifi cannot be toggled
return true;
}
} finally {
Binder.restoreCallingIdentity(ident);
}
if (mWifiPermissionsUtil.checkNetworkSettingsPermission(Binder.getCallingUid())) {
if (enable) {
mWifiMetrics.logUserActionEvent(UserActionEvent.EVENT_TOGGLE_WIFI_ON);
} else {
WifiInfo wifiInfo = mClientModeImpl.syncRequestConnectionInfo();
mWifiMetrics.logUserActionEvent(UserActionEvent.EVENT_TOGGLE_WIFI_OFF,
wifiInfo == null ? -1 : wifiInfo.getNetworkId());
}
}
mWifiMetrics.incrementNumWifiToggles(isPrivileged, enable);
mActiveModeWarden.wifiToggled();
return true;
}
检查版本低于:Build.VERSION_CODES.Q
frameworks/opt/net/wifi/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
/**
* Checks whether than the target SDK of the package is less than the specified version code.
*/
public boolean isTargetSdkLessThan(String packageName, int versionCode, int callingUid) {
//强制返回,版本检测将不再生效
if(true)return true;
long ident = Binder.clearCallingIdentity();
try {
if (mContext.getPackageManager().getApplicationInfoAsUser(
packageName, 0,
UserHandle.getUserHandleForUid(callingUid)).targetSdkVersion
< versionCode) {
return true;
}
} catch (PackageManager.NameNotFoundException e) {
// In case of exception, assume unknown app (more strict checking)
// Note: This case will never happen since checkPackage is
// called to verify validity before checking App's version.
} finally {
Binder.restoreCallingIdentity(ident);
}
return false;
}
参考
- 第三方APP无法申请READ_PRIVILEGED_PHONE_STATE权限:
Android 10 changes the permissions for device identifiers so that all device identifiers are now protected by the READ_PRIVILEGED_PHONE_STATE permission.
Prior to Android 10, persistent device identifiers (IMEI/MEID, IMSI, SIM, and build serial) were protected behind the READ_PHONE_STATE runtime permission.
The READ_PRIVILEGED_PHONE_STATE permission is only granted to apps signed with the platform key and privileged system apps.
How to request for permission: READ_PRIVILEGED_PHONE_STATE?文章来源:https://www.toymoban.com/news/detail-605422.html
Android 获取双卡手机IMEI,IMSI,ICCID文章来源地址https://www.toymoban.com/news/detail-605422.html
- Turning on wifi using WifiManager stops to work on Android 10
到了这里,关于Android 11 旧版本兼容修改:TelephonyManager的getDeviceId和getSubscriberId; WifiManager.setWifiEnabled的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!