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.
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());
}
}
I have a problem on Android 7 that not support the broadcast event "android.hardware.action.NEW_PICTURE" longer. I write now for Android 7 a JobService but it will not fire when a Picture is shot by the internal camera.
I don't know what is the problem, can everybody help me.
If any example source in the www that's for Android 7 and JobService for the replacement the broadcast "android.hardware.action.NEW_PICTURE" .
Thanks for help !
Here is my example Code:
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class ZNJobService extends JobService {
private static Zlog log = new Zlog(ZNJobService.class.getName());
static final Uri MEDIA_URI = Uri.parse("content://" + MediaStore.AUTHORITY + "/");
static final int ZNJOBSERVICE_JOB_ID = 777;
static JobInfo JOB_INFO;
#RequiresApi(api = Build.VERSION_CODES.N)
public static boolean isRegistered(Context pContext){
JobScheduler js = pContext.getSystemService(JobScheduler.class);
List<JobInfo> jobs = js.getAllPendingJobs();
if (jobs == null) {
log.INFO("ZNJobService not registered ");
return false;
}
for (int i = 0; i < jobs.size(); i++) {
if (jobs.get(i).getId() == ZNJOBSERVICE_JOB_ID) {
log.INFO("ZNJobService is registered :-)");
return true;
}
}
log.INFO("ZNJobService is not registered");
return false;
}
public static void registerJob(Context pContext){
Log.i("ZNJobService","ZNJobService init");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ) {
if (! isRegistered(pContext)) {
Log.i("ZNJobService", "JobBuilder executes");
log.INFO("JobBuilder executes");
JobInfo.Builder builder = new JobInfo.Builder(ZNJOBSERVICE_JOB_ID, new ComponentName(pContext, ZNJobService.class.getName()));
// Look for specific changes to images in the provider.
builder.addTriggerContentUri(new JobInfo.TriggerContentUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
// Also look for general reports of changes in the overall provider.
//builder.addTriggerContentUri(new JobInfo.TriggerContentUri(MEDIA_URI, 0));
JOB_INFO = builder.build();
log.INFO("JOB_INFO created");
JobScheduler scheduler = (JobScheduler) pContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
int result = scheduler.schedule(JOB_INFO);
if (result == JobScheduler.RESULT_SUCCESS) {
log.INFO(" JobScheduler OK");
} else {
log.ERROR(" JobScheduler fails");
}
}
} else {
JOB_INFO = null;
}
}
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
public boolean onStartJob(JobParameters params) {
log.INFO("onStartJob");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (params.getJobId() == ZNJOBSERVICE_JOB_ID) {
if (params.getTriggeredContentAuthorities() != null) {
for (Uri uri : params.getTriggeredContentUris()) {
log.INFO("JobService Uri=%s",uri.toString());
}
}
}
}
this.jobFinished(params,false);
return false;
}
#Override
public boolean onStopJob(JobParameters params) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
log.INFO("onStopJob");
}
return true;
}
}
In the below code you can pass the flag immediate as false for normal operation (i.e. schedule within system guidelines for an app's good behaviour). When your app's main activity starts you can pass immediate as true to force a quick retrieval of media content changes.
You should run code in the onStartJob() method in a background job. (As shown below.)
If you only want to receive media from the camera and not other sources you should just filter out URI's based on their path. So only include "*/DCIM/*". (I haven't put this in the below code though.)
Also the Android job scheduler has a policy where it denies your service if it detects over abuse. Maybe your tests have caused this in your app, so just uninstall and reinstall to reset it.
public class ZNJobService extends JobService {
//...
final Handler workHandler = new Handler();
Runnable workRunnable;
//...
public static void registerJob(Context context, boolean immediate) {
final JobInfo jobInfo = createJobInfo(context, immediate);
final JobScheduler js = context.getSystemService(JobScheduler.class);
final int result = js.schedule(jobInfo);
if (result == JobScheduler.RESULT_SUCCESS) {
log.INFO(" JobScheduler OK");
} else {
log.ERROR(" JobScheduler fails");
}
}
private static JobInfo createJobInfo(Context context, boolean immediate) {
final JobInfo.Builder b =
new JobInfo.Builder(
ZNJOBSERVICE_JOB_ID, new ComponentName(context, ZNJobService.class));
// Look for specific changes to images in the provider.
b.addTriggerContentUri(
new JobInfo.TriggerContentUri(
MediaStore.Images.Media.INTERNAL_CONTENT_URI,
JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
b.addTriggerContentUri(
new JobInfo.TriggerContentUri(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
if (immediate) {
// Get all media changes within a tenth of a second.
b.setTriggerContentUpdateDelay(1);
b.setTriggerContentMaxDelay(100);
} else {
// Wait at least 15 minutes before checking content changes.
// (Change this as necessary.)
b.setTriggerContentUpdateDelay(15 * 60 * 1000);
// No longer than 2 hours for content changes.
// (Change this as necessary.)
b.setTriggerContentMaxDelay(2 * 60 * 60 * 1000);
}
return b.build();
}
#Override
public boolean onStartJob(final JobParameters params) {
log.INFO("onStartJob");
if (params.getTriggeredContentAuthorities() != null && params.getTriggeredContentUris() != null) {
// Process changes to media content in a background thread.
workRunnable = new Runnable() {
#Override
public void run() {
yourMethod(params.getTriggeredContentUris());
// Reschedule manually. (The 'immediate' flag might have changed.)
jobFinished(params, /*reschedule*/false);
scheduleJob(ZNJobService.this, /*immediate*/false);
}};
Postal.ensurePost(workHandler, workRunnable);
return true;
}
// Only reschedule the job.
scheduleJob(this, /*immediate*/false);
return false;
}
#Override
public boolean onStopJob(final JobParameters params) {
if (workRunnable != null) {
workHandler.removeCallbacks(workRunnable);
workRunnable = null;
}
return false;
}
private static void yourMethod(Uri[] uris) {
for (Uri uri : uris) {
log.INFO("JobService Uri=%s", uri.toString());
}
}
}
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; }
}
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;
}
}
I have 2 android intent objects that can be persisted as URLs and then rehydrated back into intent objects. I'm wondering what is the most effective way to compare any 2 intent objects to ensure that they end up resolving to the same activity with the same parameters etc. Using intent.filterEquals does this, but it does not include the extras.
Currently my code for the equals method looks like this:
Intent a = Intent.parseUri(this.intentUrl,
Intent.URI_INTENT_SCHEME);
Intent b = Intent.parseUri(other.intentUrl,
Intent.URI_INTENT_SCHEME);
if (a.filterEquals(b)) {
if (a.getExtras() != null && b.getExtras() != null) {
for (String key : a.getExtras().keySet()) {
if (!b.getExtras().containsKey(key)) {
return false;
} else if (!a.getExtras().get(key)
.equals(b.getExtras().get(key))) {
return false;
}
}
}
// all of the extras are the same so return true
return true;
} else { return false; }
But is there a better/cleaner way?
That's probably as good as it gets, at least conceptually. However, I don't think your algorithm covers cases where b has a key that a does not.
I'd get both keySet() values and run an equals() on those, to confirm they both have the same keys. Then, iterate over one and run equals() on the value pair.
Improving upon #aostiles' answer by adding the missing return statement and also conditions to compare arrays in extras:
private boolean intentsAreEqual (Intent a, Intent b)
{
if (a.filterEquals(b)) {
if (a.getExtras() != null && b.getExtras() != null) {
// check if the keysets are the same size
if (a.getExtras().keySet().size() != b.getExtras().keySet().size()) return false;
// compare all of a's extras to b
for (String key : a.getExtras().keySet()) {
if (!b.getExtras().containsKey(key)) {
return false;
}
else if (a.getExtras().get(key).getClass().isArray() && b.getExtras().get(key).getClass().isArray()) {
if (!Arrays.equals((Object[]) a.getExtras().get(key), (Object[]) b.getExtras().get(key))) {
return false;
}
}
else if (!a.getExtras().get(key).equals(b.getExtras().get(key))) {
return false;
}
}
// compare all of b's extras to a
for (String key : b.getExtras().keySet()) {
if (!a.getExtras().containsKey(key)) {
return false;
}
else if (b.getExtras().get(key).getClass().isArray() && a.getExtras().get(key).getClass().isArray()) {
if (!Arrays.equals((Object[]) b.getExtras().get(key), (Object[]) a.getExtras().get(key))) {
return false;
}
}
else if (!b.getExtras().get(key).equals(a.getExtras().get(key))) {
return false;
}
}
return true;
}
if (a.getExtras() == null && b.getExtras() == null)
{
return true;
}
// either a has extras and b doesn't or b has extras and a doesn't
return false;
}
else
{
return false;
}
}
This is pretty much an implementation of what CommonsWare suggested combined with Ben's code but also covers the case where either a has extras and b does not or b has extras and a does not.
private boolean areEqual(Intent a, Intent b) {
if (a.filterEquals(b)) {
if (a.getExtras() != null && b.getExtras() != null) {
// check if the keysets are the same size
if (a.getExtras().keySet().size() != b.getExtras().keySet().size()) return false;
// compare all of a's extras to b
for (String key : a.getExtras().keySet()) {
if (!b.getExtras().containsKey(key)) {
return false;
} else if (!a.getExtras().get(key).equals(b.getExtras().get(key))) {
return false;
}
}
// compare all of b's extras to a
for (String key : b.getExtras().keySet()) {
if (!a.getExtras().containsKey(key)) {
return false;
} else if (!b.getExtras().get(key).equals(a.getExtras().get(key))) {
return false;
}
}
}
if (a.getExtras() == null && b.getExtras() == null) return true;
// either a has extras and b doesn't or b has extras and a doesn't
return false;
} else {
return false;
}
}