Note that I'm talking about Android Lollipop. For android 6.0 we can use method canDrawOverlays() to check that SYSTEM_ALERT_WINDOW is granted or not.
With Android Lollipop, almost devices grant this permission by default. But on some devices of Xiaomi, Meizu.. it is not granted. Users need to go to the App info to allow it.
How can we check it programmatically to warn users?
in MIUI use
public static boolean isMiuiFloatWindowOpAllowed(#NonNull Context context) {
final int version = Build.VERSION.SDK_INT;
if (version >= 19) {
return checkOp(context, OP_SYSTEM_ALERT_WINDOW); //See AppOpsManager.OP_SYSTEM_ALERT_WINDOW=24 /*#hide/
} else {
return (context.getApplicationInfo().flags & 1<<27) == 1;
}
}
public static boolean checkOp(Context context, int op, String packageName, int uid) {
final int version = Build.VERSION.SDK_INT;
if (version >= 19) {
AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
try {
return (AppOpsManager.MODE_ALLOWED == (Integer) ReflectUtils.invokeMethod(manager, "checkOp", op, uid, packageName));
} catch (Exception e) {
e.printStackTrace();
}
} else {
Flog.e("Below API 19 cannot invoke!");
}
return false;
}
ReflectUtils.java
public static Object invokeMethod(#NonNull Object receiver, String methodName, Object... methodArgs) throws Exception {
Class<?>[] argsClass = null;
if (methodArgs != null && methodArgs.length != 0) {
int length = methodArgs.length;
argsClass = new Class[length];
for (int i=0; i<length; i++) {
argsClass[i] = getBaseTypeClass(methodArgs[i].getClass());
}
}
Method method = receiver.getClass().getMethod(methodName, argsClass);
return method.invoke(receiver, methodArgs);
}
Reflection is risky because you take things for granted...and things can change in future versions of Android. The following method only uses reflection if the proper way fails.
#SuppressLint("NewApi")
public static boolean canDrawOverlayViews(Context con){
if(Build.VERSION.SDK_INT< Build.VERSION_CODES.LOLLIPOP)
return true;
try {
return Settings.canDrawOverlays(con);
}
catch(NoSuchMethodError e){
return canDrawOverlaysUsingReflection(con);
}
}
public static boolean canDrawOverlaysUsingReflection(Context context) {
try {
AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
Class clazz = AppOpsManager.class;
Method dispatchMethod = clazz.getMethod("checkOp", new Class[] { int.class, int.class, String.class });
//AppOpsManager.OP_SYSTEM_ALERT_WINDOW = 24
int mode = (Integer) dispatchMethod.invoke(manager, new Object[] { 24, Binder.getCallingUid(), context.getApplicationContext().getPackageName() });
return AppOpsManager.MODE_ALLOWED == mode;
} catch (Exception e) { return false; }
}
Related
I need to check whether any fake app using location in developer settings. So that I need to make user to change it to developer setting.
But I need to use without using location. I tried this method but this displays not enabled toast message eventhough I enabled the fake app location.
public static boolean isMockLocationEnabled(Context context)
{
boolean isMockLocation = false;
try {
//if marshmallow
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
AppOpsManager opsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
isMockLocation = (opsManager.checkOp(AppOpsManager.OPSTR_MOCK_LOCATION, android.os.Process.myUid(), BuildConfig.APPLICATION_ID)== AppOpsManager.MODE_ALLOWED);
} else {
// in marshmallow this will always return true
isMockLocation = !android.provider.Settings.Secure.getString(context.getContentResolver(), "mock_location").equals("0");
}
} catch (Exception e) {
return isMockLocation;
}
return isMockLocation;
}
MainActivity:
#Override
protected void onStart() {
super.onStart();
if (AppUtils.isMockLocationEnabled(this)) {
Log.e("Location..............", "enabled");
} else {
Log.e("Location..............", "not enabled"); //it always goes here in 8.0
}
}
So without location, how to pass? Because I just need to check whether fake location is using or not?
//location.isFromMockProvider(); // without location how to use?
So any other solution?
You can check whether the Mock options is ON:
if (Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.ALLOW_MOCK_LOCATION).equals("0"))
return false;
else
return true;
Or you can detect if there is an application which is using Mock:
boolean isExisted = false;
PackageManager pm = getPackageManager();
List<ApplicationInfo> packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo applicationInfo : packages) {
try {
PackageInfo packageInfo = pm.getPackageInfo(applicationInfo.packageName,
PackageManager.GET_PERMISSIONS);
String[] requestedPermissions = packageInfo.requestedPermissions;
if (requestedPermissions != null) {
for (int i = 0; i < requestedPermissions.length; i++) {
if (requestedPermissions[i]
.equals("android.permission.ACCESS_MOCK_LOCATION")
&& !applicationInfo.packageName.equals(context.getPackageName())) {
isExisted = true;
break;
}
}
}
} catch (NameNotFoundException e) {
Log.e("Got exception " , e.getMessage());
}
}
private boolean isEphemeralAllowed(
Intent intent, List<ResolveInfo> resolvedActivities, int userId,
boolean skipPackageCheck) {
// Short circuit and return early if possible.
if (isEphemeralDisabled()) {
return false;
}
final int callingUser = UserHandle.getCallingUserId();
if (callingUser != UserHandle.USER_SYSTEM) {
return false;
}
if (mEphemeralResolverConnection == null) {
return false;
}
if (intent.getComponent() != null) {
return false;
}
if ((intent.getFlags() & Intent.FLAG_IGNORE_EPHEMERAL) != 0) {
return false;
}
if (!skipPackageCheck && intent.getPackage() != null) {
return false;
}
final boolean isWebUri = hasWebURI(intent);
private boolean isEphemeralDisabled() {
// ephemeral apps have been disabled across the board
if (DISABLE_EPHEMERAL_APPS) {
return true;
}
// system isn't up yet; can't read settings, so, assume no ephemeral apps
if (!mSystemReady) {
return true;
}
// we can't get a content resolver until the system is ready; these checks must happen last
final ContentResolver resolver = mContext.getContentResolver();
if (Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0) {
return true;
}
return Secure.getInt(resolver, Secure.WEB_ACTION_ENABLED, 1) == 0;
}
For Android 7.0, DISABLE_EPHEMERAL_APPS default is true
private static final boolean DISABLE_EPHEMERAL_APPS = true;
But in Android 7.1, Google enabled Instant apps support: https://android.googlesource.com/platform/frameworks/base.git/+/7ef97b6624054fff0d712d85336a45eee70bcc3f%5E%21/#F0
for isEphemeralAllowed method, if call resolveIntent, most of intents will call isEphemeralAllowed method, so this will cause PackageManager service user binder call settingProvider, and will probability cause a deadlock.
I am trying to get a list of networks on Android devices that have multiple SIM cards "dual sim."
I use the TelephonyManager class but the method getNetworkType only returns the network for the first sim "sim 1."
There's no API for this before Android Android 5.1 (API22). But then you have SubscriptionManager and its getActiveSubscriptionInfoList()
I have found a posible solution. I have used the android reflection to call TelephonyManager methods for example if i want the data Network I can use getDataNetworkType as follows:
getNetworkTypeReflection(telephonyManager, "getDataNetworkType", slot, false);
private static String getNetworkTypeReflection(final TelephonyManager telephony, final String predictedMethodName, final int slotID, final boolean isPrivate) {
String result = null;
try {
final Class<?> telephonyClass = Class.forName(telephony.getClass().getName());
final Class<?>[] parameter = new Class[1];
parameter[0] = int.class;
final Method getSubtecnology;
if (slotID != -1) {
if (isPrivate) {
getSubtecnology = telephonyClass.getDeclaredMethod(predictedMethodName, parameter);
} else {
getSubtecnology = telephonyClass.getMethod(predictedMethodName, parameter);
}
} else {
if (isPrivate) {
getSubtecnology = telephonyClass.getDeclaredMethod(predictedMethodName);
} else {
getSubtecnology = telephonyClass.getMethod(predictedMethodName);
}
}
final Object obPhone;
final Object[] obParameter = new Object[1];
obParameter[0] = slotID;
if (getSubtecnology != null) {
if (slotID != -1) {
obPhone = getSubtecnology.invoke(telephony, obParameter);
} else {
obPhone = getSubtecnology.invoke(telephony);
}
if (obPhone != null) {
result = obPhone.toString();
}
}
} catch (Exception e) {
//e.printStackTrace();
return null;
}
return result;
}
The problem is that this option only works on Android 5.1 (API22) but only in some device in others you need Android 7.0 (API24).
If anyone has other options are welcome.
I need to check if the option "Install apps from unknown sources" is enabled or disabled. However, INSTALL_NON_MARKET_APPS was deprecated in API 17. Is there a new alternative to check this? This is the old way of checking:
boolean canInstallFromOtherSources = Settings.Secure.getInt(Settings.Secure.INSTALL_NON_MARKET_APPS) == 1;
Edit:
boolean unknownSource = false;
if (Build.VERSION.SDK_INT < 17) {
unknownSource = Settings.Secure.getInt(null, Settings.Secure.INSTALL_NON_MARKET_APPS, 0) == 1;
} else {
unknownSource = Settings.Global.getInt(null, Settings.Global.INSTALL_NON_MARKET_APPS, 0) == 1;
}
As the documentation for Settings.Secure.INSTALL_NON_MARKET_APPS points out, the replacement is Settings.Global.INSTALL_NON_MARKET_APPS.
Settings.Secure.INSTALL_NON_MARKET_APPS is deprecated in API 17 thus if you have minimalSDK set lower than 17, it is not directly reachable and reflection has to be used.
My solution:
public class BackwardCompatibility {
private static Class<?> settingsGlobal;
/**
* Returns Settings.Global class for reflective calls.
* Global is a nested class of the Settings, has to be done in a special way.
*
* #return
*/
public static Class<?> getSettingsGlobal(){
if (settingsGlobal!=null){
return settingsGlobal;
}
try {
Class<?> master = Class.forName("android.provider.Settings");
Class<?>[] classes = master.getClasses();
for(Class<?> cls : classes){
if (cls==null) {
continue;
}
if ("android.provider.Settings$Global".equals(cls.getName())){
settingsGlobal = cls;
return settingsGlobal;
}
}
return null;
} catch(Exception ex){
Log.e(TAG, "Reflective call not successfull", ex);
}
return null;
}
/**
* Determines whether installing Android apks from unknown sources is allowed.
*
* #param ctxt
* #return
*/
public static boolean isUnknownSourceInstallAllowed(Context ctxt){
try {
boolean unknownSource = false;
if (Build.VERSION.SDK_INT < 17) {
unknownSource = Settings.Secure.getInt(ctxt.getContentResolver(), Settings.Secure.INSTALL_NON_MARKET_APPS, 0) == 1;
} else {
// Has to use reflection since API 17 is not directly reachable.
// Original call would be:
// unknownSource = Settings.Global.getInt(ctxt.getContentResolver(), Settings.Global.INSTALL_NON_MARKET_APPS, 0) == 1;
//
Class<?> c = getSettingsGlobal();
Method m = c.getMethod("getInt", new Class[] { ContentResolver.class, String.class, int.class });
// Obtain constant value
Field f = c.getField("INSTALL_NON_MARKET_APPS");
final String constVal = (String) f.get(null);
unknownSource = Integer.valueOf(1).equals((Integer) m.invoke(null, ctxt.getContentResolver(), constVal, 0));
}
return unknownSource;
} catch(Exception e){
// Handle this as you like.
Log.w(TAG, "Cannot determine if installing from unknown sources is allowed", e);
}
return false;
}
}
I'm trying to check if the user has enabled/disabled data roaming. All I found so far is that you can check whether or not the user is currently IN roaming, using TelephonyManager.isNetworkRoaming() and NetworkInfo.isRoaming(), but they are not what I need.
Based on Nippey's answer, the actual piece of code that worked for me is:
public Boolean isDataRoamingEnabled(Context context) {
try {
// return true or false if data roaming is enabled or not
return Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.DATA_ROAMING) == 1;
}
catch (SettingNotFoundException e) {
// return null if no such settings exist (device with no radio data ?)
return null;
}
}
You can request the state of the Roaming-Switch via
ContentResolver cr = ContentResolver(getCurrentContext());
Settings.Secure.getInt(cr, Settings.Secure.DATA_ROAMING);
See: http://developer.android.com/reference/android/provider/Settings.Secure.html#DATA_ROAMING
public static final Boolean isDataRoamingEnabled(final Context application_context)
{
try
{
if (VERSION.SDK_INT < 17)
{
return (Settings.System.getInt(application_context.getContentResolver(), Settings.Secure.DATA_ROAMING, 0) == 1);
}
return (Settings.Global.getInt(application_context.getContentResolver(), Settings.Global.DATA_ROAMING, 0) == 1);
}
catch (Exception exception)
{
return false;
}
}
Updated function to account for API deprecation. It is now replaced with:
http://developer.android.com/reference/android/provider/Settings.Global.html#DATA_ROAMING
public static boolean IsDataRoamingEnabled(Context context) {
try {
// return true or false if data roaming is enabled or not
return Settings.Global.getInt(context.getContentResolver(), Settings.Global.DATA_ROAMING) == 1;
}
catch (SettingNotFoundException e) {
return false;
}
}