I'm encountering such a scenario in Android.
App A has permission PERM to get some information. Instead of directly getting this information, it sends an intent to one BroadCastReceiver inside App B (of course B has PERM). In order to ensure all such apps like A has the permission, I use a context.checkCallingPermission (with the help of this answer).
However it always fails.
public void onReceive(Context context, Intent intent) {
String info;
String perm = Manifest.permission.READ_PHONE_STATE;
Log.i(TAG, "callPid="+ Binder.getCallingPid()+" myPid="+ Process.myPid()); /// SAME PID
if (context.checkCallingPermission(perm) != PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "not granted " + perm);
info = "denied perm " + perm;
} else {
Log.i(TAG, perm + " already granted");
info = dumpDeviceInfoImpl(context);
}
}
Specially, the comment line always return SAME pid (B's), when I send intent from A. I can confirm A and B have different PIDs.
update
Previously I was thinking it is due to BroadCastReceiver, so I tried to startActivity from A and then start an activity in B. Once again, during checkCallingPermission, it fails again. Surprisingly, still returning the SAME pid.
So what's the correct way to use checkCallingPermission?
use this methdos
checkCallingPermission(Context context, String permission, String packageName)
Checks whether the IPC you are handling has a given permission and whether the app op that corresponds to this permission is allowed.
Related
Previously I was doing this by the standard 'startActivityForResult()' approach, catching the result in onActivityResult() callback. And it worked. But now the method has a big-strike though since it is deprecated so I am trying to use the new launcher approach. However, I always get the same result regardless of what the user does. result.getResultCode() always returns a failure. Double checking with Settings.canDrawOverlays(context) also returns failure even when the permission has been granted. Though far later in the code when I check using Settings.canDrawOverlays(context) it returns success. Putting in a delay in the launcher callback does not solve the inconsistency.
Here is my Launcher registration and callback:
ActivityResultLauncher<Intent> activityResultOverlayLauncher =
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<>()
{
#Override
public void onActivityResult(ActivityResult result)
{
if (result.getResultCode() != Activity.RESULT_OK)
{
HaHStatics.debugLog(TAG, context, "d", "HH2: Permission for device discovery popups not given");
Toast.makeText(context, "Overlay Permission not given.", Toast.LENGTH_LONG).show();
// Double checking with Settings.canDrawOverlays(context) also always returns failure
}
else
{
HaHStatics.debugLog(TAG, context, "d", "HH2: Permission for device discovery popups given.");
}
// Do next thing
}
});
And here is the request code:
#TargetApi(Build.VERSION_CODES.M)
private void getSystemWindowAlertPermission()
{
HaHStatics.debugLog(TAG, context, "d", "HH2: Checking for permission to allow Alert popups");
if (isVersionM_Plus && !Settings.canDrawOverlays(this)) // isVersionM_Plus checks build >= OS 6
{
final Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
try
{
activityResultOverlayLauncher.launch(intent);
}
catch (ActivityNotFoundException e)
{
Log.e(TAG, e.getMessage());
}
}
}
The call to initiate the settings activity works fine in all versions of Android though version 11 requires a two-step navigation by the user.
The only thing I can think of is that I am using the wrong contract OR that the system is messing up and not setting what it needs to set until I return from the launcher callback.
I am at a loss. Any ideas of what I am doing wrong?
PS: this approach works great for asking all the Bluetooth and Location and background permissions needed to do discovery, scanning, and auto reconnects from versions OS 6 to 12.
I have been through with many Google solutions but not any solution is able to resolve my problem. What I am trying to do is firing a window for user to enter pin/password while he is trying to uninstall my application.
I saw this link, but I am facing 2 issues with this solution these are as follow:
I registered broadcast receiver in My Application, when I open "My Application" in settings, it does not seems working but working fine with other apps. My Code below:
public void onReceive(Context context, Intent intent) {
/// fetching package names from extras
String[] packageNames = intent.getStringArrayExtra("android.intent.extra.PACKAGES");
if(packageNames!=null){
for(String packageName: packageNames){
if(packageName!=null && packageName.equals("com.android.systemapplication")){
// User has selected our application under the Manage Apps settings
// now initiating background thread to watch for activity
new ListenActivities(context).start();
}
}
}
}
When I replaced package name with some different package name that settings screen can detect in broadcast receiver then in run(thread) it is not detecting uninstaller activity, Please see my ListenActivities as below:
public void run(){
Looper.prepare();
while(!exit){
// get the info from the currently running task
List< ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(MAX_PRIORITY);
String activityName = taskInfo.get(0).topActivity.getClassName();
Log.d("topActivity", "CURRENT Activity ::"
+ activityName);
// here "activityName" is "com.android.launcher" if I select
// package name it is "com.android.launcher2.launcher".
if (activityName.equals("com.android.packageinstaller.UninstallerActivity")) {
// User has clicked on the Uninstall button under the Manage Apps settings
//do whatever pre-uninstallation task you want to perform here
exit = true;
Toast.makeText(context, "Done with preuninstallation tasks... Exiting Now", Toast.LENGTH_SHORT).show();
} else if(activityName.equals("com.android.settings.ManageApplications")) {
// back button was pressed and the user has been taken back to Manage Applications window
// we should close the activity monitoring now
exit=true;
}
}
Looper.loop();
}
I saw many solutions but no success. if it is deprecated then please show some Android documentation because I can see antivirus can do same as what I am asking for. Code would be more helpful. ? Any help would be helpful.
I have a BroadcastReceiver (Android 4.1) which must test for the existence of a file located in the external storage area. The receiver doesn't need to read or write to the file; just test for its existence.
I haven't found anything in the Android documentation which indicates that BroadcastReceivers cannot access external files, yet the code below always returns false.
The logcat output shows getExternalStorageState() returns "mounted" and I can access the file using an ordinary App, just not in the Receiver. No exceptions are thrown.
public class FileCheckReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
boolean b = checkFile();
Log.d(TAG, "FileCheckReceiver: " + b);
}
boolean checkFile() {
String state = Environment.getExternalStorageState();
Log.d(TAG, "FileCheckReceiver, Environment.getExternalStorageState: " + state);
String name = "file.txt";
File f = new File(Environment.getExternalStorageDirectory() + "/" + name);
try
{
if (f.exists()) {
System.out.println(f.getCanonicalPath().toString());
return true;
}
}catch(Exception e) {
e.printStackTrace();
}
return false;
}
}
Are BroadcastReceivers stopped from accessing files?
A good approach would to be start a new Service from the BroadcastReceiver to handle tasks that you wish to perform.
Other things:
Make sure you have declared the receiver in manifest.xml
Also you need to consider the following as well.
As of Android 3.1 the Android system will by default exclude all BroadcastReceiver from receiving intents if the corresponding application has never been started by the user or if the user explicitly stopped the application via the Android menu (in Manage Application).
This is an additional security features as the user can be sure that only the applications he started will receive broadcast intents.
I eventually discovered that System Apps (which my app was) do not have any access to files stored on external storage.
Moving my BroadcastReceiver to another (non-system) APK fixed the problem.
Is there a way for an Activity to find out who (i.e. class name) has sent an Intent? I'm looking for a generic way for my Activity to respond to a received intent by sending one back to the sender, whoever that may be.
There may be another way, but the only solution I know of is having Activity A invoke Activity B via startActivityForResult(). Then Activity B can use getCallingActivity() to retrieve Activity A's identity.
Is it an external app you receive the intent from? You could use the getReferrer() method of the activity class
A simple example: I opened google map app to share some location with my app by using the share option of google maps. Then my app opens and this method call in the Activity:
this.getReferrer().getHost()
will return:
com.google.android.apps.maps
see documentation here: https://developer.android.com/reference/android/app/Activity.html#getReferrer()
Note that this requires API 22. For older Android versions see answer from ajwillliams
A technique I use is to require the application sending the relevant Intent to add a PendingIntent as a Parcelable extra; the PendingIntent can be of any type (service, broadcast, etc.). The only thing my service does is call PendingIntent.getCreatorUid() and getCreatorPackage(); this information is populated when the PendingIntent is created and cannot be forged by the app so I can get the info about an Intent's sender.
Only caveat is that solution only works from Jellybean and later which is my case.
Hope this helps,
This isn't incredibly direct but you can get a list of the recent tasks from ActivityManager. So the caller would essentially be the task before yours and you can fetch info on that task.
Example usage:
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.RecentTaskInfo> recentTasks = am.getRecentTasks(10000,ActivityManager.RECENT_WITH_EXCLUDED);
The above will return a list of all the tasks from most recent (yours) to the limit specified. See docs here for the type of info you can get from a RecentTaskInfo object.
Generally you don't need to know this. If the calling activity uses startActivityForResult(Intent, int), the callee can use setResult(int, Intent) to specify an Intent to send back to the caller. The caller will receive this Intent in its onActivityResult(int, int, Intent) method.
Based on your question, since you want to send an intent back to the sender startActivityForResult is a better choice than what I am going to suggest. But I needed to start activity B when a notification is clicked by the user and execute some code in activity B only if the sender activity is activity A. This is how I did it quite simply.
Inside Activity A:
String senderName = this.getClass().getSimpleName();
Intent clickIntent = new Intent(ActivityA.this, ActivityB.class);
clickIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
clickIntent.putExtra("SENDER_CLASS_NAME", senderName);
//I use PendingIntent to start Activity B but you can use what you like such as this.startActivity(clickIntent);
PendingIntent.getActivity(ActivityA.this, NOTIFICATION_ID, clickIntent, PendingIntent.FLAG_ONE_SHOT);
Inside Activity B:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
if(bundle.containsKey("SENDER_CLASS_NAME")){
String senderName = bundle.getString("SENDER_CLASS_NAME");
//Execute some code
Log.d("GCM", "Notifications clicked");
}
}
}
}
In my case, neither the accepted here and another most voted answer works perfectly.
Activity.getCallerActivity() works only for the sender which starts your activity by startActivityForResult, meaning that if the sender is also in your app and you have full control, it works, but not every external app starts others in that way.
Another most voted answer provides the solution for external app, but it too has issue. First I would prefer getAuthority() instead of getHost(), secondly, if the sender is a browser kind of app, like Chrome, both host and authority will give you the browsing web page's address host, such as www.google.com, instead of the app itself. So it depends on how you define 'sender', if you need to find out which web page starts you, the authority/host is good enough, but if you need to find out which app starts you, I am afraid authority/host can be trusted only when getScheme() gives you android-app instead of http.
Use UsageStatsManager and the old RecentTaskInfo to get the intent sender for OnCreate or onNewIntent:
public static String getTopMostThirdPartyPackage(Context context) {
String thisPak = null, tmp, top = null;
try {
thisPak = context.getPackageName();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
UsageStatsManager man = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
long now = System.currentTimeMillis();
UsageEvents uEvts = man.queryEvents(now - 5000,now); // query in 5 sec
UsageEvents.Event e = new UsageEvents.Event();
while (uEvts.getNextEvent(e)){
tmp = e.getPackageName();
if (!thisPak.equals(tmp)) {
top = tmp;
break;
}
}
} else {
ActivityManager man = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RecentTaskInfo> tasks = man.getRecentTasks(3, 0);
for(ActivityManager.RecentTaskInfo info:tasks) {
tmp = info.baseIntent.getComponent().getPackageName();
if (!thisPak.equals(tmp)) {
top = tmp;
break;
}
}
}
} catch (Exception e) {
}
return top;
}
permissions :
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<uses-permission android:name="android.permission.GET_TASKS" />
intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
startActivity(intent);
I want to uninstall my application on button click. For this I am using following code.
Uri packageURI = Uri.parse("package:"+packageName);
Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
startActivity(uninstallIntent);
It gives me result, but I want to delete directly without click on "Ok" button of dialog with message "This application will be Uninstalled".
I just want uninstalling application directly.
Uninstalling without user confirmation is not allowed to 3rd party applications.
As xDragonZ points out, a root process can crudely do this by literally removing the directory and leaving the package manager to deal with the loss, but that's not a very widely deployable solution, since AFAIK no devices ship with that capability for apps to run their own root helper process - that's a risky aftermarket modification.
Yes it is possible to uninstall a package in Android. Moreover you can also skip asking user to press OK button on uninstall screen. You can do it by using Accessibility service in Android.
public class MyAccessibilityService extends AccessibilityService {
private static final String TAG = MyAccessibilityService.class
.getSimpleName();
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
Log.i(TAG, "ACC::onAccessibilityEvent: " + event.getEventType());
//TYPE_WINDOW_STATE_CHANGED == 32
if (AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == event
.getEventType()) {
AccessibilityNodeInfo nodeInfo = event.getSource();
Log.i(TAG, "ACC::onAccessibilityEvent: nodeInfo=" + nodeInfo);
if (nodeInfo == null) {
return;
}
List<AccessibilityNodeInfo> list = nodeInfo
.findAccessibilityNodeInfosByViewId("com.android.settings:id/left_button");
for (AccessibilityNodeInfo node : list) {
Log.i(TAG, "ACC::onAccessibilityEvent: left_button " + node);
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
list = nodeInfo
.findAccessibilityNodeInfosByViewId("android:id/button1");
for (AccessibilityNodeInfo node : list) {
Log.i(TAG, "ACC::onAccessibilityEvent: button1 " + node);
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
}
#Override
public void onServiceConnected() {
Log.i(TAG, "ACC::onServiceConnected: ");
}
#Override
public void onInterrupt() {
// TODO Auto-generated method stub
}
}
You should first look into the Android native PackageInstaller. I would recommendating you to update all the code you use.
Next step is to inspect PackageInstaller which is an normal class. You will find that uninstall function there. The bad news is that this needs Manifest.permission.DELETE_PACKAGES permission and its only granted to system apps. This means that this is not available directly to other developers. But we can access it using device owner permission.
This requires:
Android 6.0 or newer
Device owner permission to uninstall the package
Generally the DELETE_PACKAGES permission says:
Allows an application to delete packages.
Not for use by third-party applications.
Once your app gets the device owner permission, you can uninstall an package like this:
String appPackage = "com.your.app.package";
Intent intent = new Intent(getApplicationContext(),
getApplicationContext().getClass()); //getActivity() is undefined!
PendingIntent sender = PendingIntent.getActivity(getActivity(), 0, intent, 0);
PackageInstaller mPackageInstaller =
getActivity().getPackageManager().getPackageInstaller();
mPackageInstaller.uninstall(appPackage, sender.getIntentSender());
The code used available here:
PackageInstaller "Silent install and uninstall of apps by Device Owner” - Android M Preview