I want to develop an app that can receive broadcast about the other apps being installed or removed. So far, I tried the code below but, it only provides a broadcast event when an installation on deletion occurs, it does not provide info about the other apps beibg installed or removed. So, is there a way to get the package name of the newly installed application.
in manifset:
receiver android:name=".apps.AppListener">
<intent-filter android:priority="100">
<action android:name="android.intent.action.PACKAGE_INSTALL"/>
<action android:name="android.intent.action.PACKAGE_ADDED"/>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<data android:scheme="package"/>
</intent-filter>
</receiver>
in AppListener:
public class AppListener extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// there is a broadcast event here
// but how to get the package name of the newly installed application
Log.v(TAG, "there is a broadcast");
}
}
Addition:
This is deprecated for Api 14 or upper.
<action android:name="android.intent.action.PACKAGE_INSTALL"/>
Package name is embedded into the Intent you are receiving in onReceive() method. You can read it using below code snippet :
Uri data = broadcastIntent.getData();
String installedPackageName = data.getEncodedSchemeSpecificPart();
For PACKAGE_ADDED, PACKAGE_REMOVED and PACKAGE_REPLACED, you can get package name using above code.
In case of application update, you will get 2 broadcasts back to back as below :
1. PACKAGE_REMOVED 2. PACKAGE_REPLACED
In case of application update, PACKAGE_REMOVED intent will contain extra boolean to differentiate between app removal and app update.
You can read this boolean as below :
boolean isReplacing = broadcastIntent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
Just to get package name you are calling PackageManagerService api is overhead. Must avoid it.
Hope, this will help you.
Intent that you get in onReceive function contains the information related to the package being added or removed.
intent.getData().toString()
You can get the application name by this function:
private String getApplicationName(Context context, String data, int flag) {
final PackageManager pckManager = context.getPackageManager();
ApplicationInfo applicationInformation;
try {
applicationInformation = pckManager.getApplicationInfo(data, flag);
} catch (PackageManager.NameNotFoundException e) {
applicationInformation = null;
}
final String applicationName = (String) (applicationInformation != null ? pckManager.getApplicationLabel(applicationInformation) : "(unknown)");
return applicationName;
}
For more info check:
Created BroadcastReceiver which displays application name and version number on install/ uninstall of any application?
There are 2 options:
1)
If you are asking if you can receive a broadcast when your same app is being uninstalled, then the answer is:
This cannot be done, unless you are a System app.
Android does not notify the app when it is going to be installed. This would be a security risk, as it would enable apps to prevent uninstallation.
2)
If you are asking about when other apps are being uninstalled, then this might be a duplicate of:
Android: How to get the installed App information using Broadcast receiver
How to find the package name which has been uninstalled when using Intent.ACTION_PACKAGE_REMOVED
Created BroadcastReceiver which displays application name and version number on install/ uninstall of any application?
Related
I uninstall an app programatically in Android, using an Intent, like you see below:
Uri packageURI = Uri.parse("package:"+packageName);
Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
startActivity(uninstallIntent);
Which redirects to a prompt that asks if you want to uninstall the app.
Usually after that you can see a toast in the bottom of the screen saying the app was uninstalled. But I want to be able to be notified so I can remove an uninstall button in a view.
How can I know in the code when the uninstall of the package has been completed? Or if an error occurred? Or even, if user clicked "ok" to uninstall or "cancel" if he changed his mind, how can I know?
Is it possible to know any of this? Is there an alternative way to uninstall a package (without being a system app) and be notified?
Thank you for reading. Lemme know if you need any more information.
Well I ended up finding a solution, when a package is removed there is an intent that can be picked up by a receiver.
In my AndroidManifest
<application
<!--...-->
<receiver
android:name=".UninstalledBroadcastReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<data android:scheme="package"/>
</intent-filter>
</receiver>
</application>
Create a UninstalledBroadcastReceiver class that extends a normal BroadcastReceiver
public class UninstalledBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// handle the intent here
}
}
You can check for instance if your package name of the app that was installed matches your own and do something with it. In my case, I put some static fields in the UninstalledBroadcastReceiver, including an interface so that I could perform some callbacks. Don't think you can pass fields in the constructor, since the object is created when the intent is received.
First of all, I have researched a lot about my issue, but I could not find a proper solution so I am posting my query here. Hope to get a better solution to the issue:
I have a requirement where I need to ask for password to the user before user deletes my app from settings or from any other application like MyAppSharer. I have found one solution where I can successfully be able to call my activity when user clicks on Uninstall button. I have applied trick here, and calling service. In service, I run timer which runs every 1 second and in that one second, it checks for top most activity of running task. This is running perfectly as per expected.
Now, my issue is, this activity apppears on each of application user tries to uninstall. I need that the activity which I call, should only appear for my application when user tries to uninstall my application.
Here is my code:
public static final String PACKAGE_INSTALLER = "com.android.packageinstaller";
public static final String PACKAGE_INSTALLER_UNINSTALL_ACTIVITY = "com.android.packageinstaller.UninstallerActivity";
alarmTimer.scheduleAtFixedRate(new TimerTask() {
public void run() {
mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);;
ComponentName topActivity = mActivityManager.getRunningTasks(1).get(0).topActivity;
final String packageName = topActivity.getPackageName();
String className = topActivity.getClassName();
Log.v(TAG, "packageName:" + packageName);
Log.v(TAG, "className:" + className);
if (PACKAGE_INSTALLER.equals(packageName)
&& PACKAGE_INSTALLER_UNINSTALL_ACTIVITY.equals(className)) {
//Here I need to apply one condition where package name received to be matched with my package name. But I am not sure how to fetch package name of selected application for uninstalling
//To Cancel Existing UninstallerActivity and redirect user to home.
Intent homeIntent = new Intent();
homeIntent.setAction(Intent.ACTION_MAIN);
homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
homeIntent.addCategory(Intent.CATEGORY_HOME);
startActivity(homeIntent);
//To open my activity
Intent loginActivity = new Intent(UninstallService.this, Act_Login.class);
loginActivity.putExtra(Constants.KEY_IS_FROM_SERVICE, true);
loginActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(loginActivity);
}
}
}, 0, 1000);
you should try something like the following :
1st - declare your broadcast recevier in the Manifest file , that will listen to QUERY_PACKAGE_RESTART :
<receiver android:name=".UninstallReceiver">
<intent-filter android:priority="999999">
<action android:name="android.intent.action.QUERY_PACKAGE_RESTART" />
<data android:scheme="package" />
</intent-filter>
</receiver>
2nd - your UnunstallIntentReceiver java class like the following :
public class UninstallReceiver extends BroadcastReceiver{
#Override
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("application_package")){
// start your activity here and ask the user for the password
}
}
}
}
}
and please give me some feedback
Hope That Helps.
If this is a corporate requirement (if you want to block a regular user from uninstalling your app, no chance, thanks Google for protecting us from bad devs), you should create a device administrator application. This way, although the user still can delete the app, it's one extra step if you want to prevent accidental erasing.
Before deleting your app, if it's enabled as device admin, the user must first disable the app as administrator, and the app receives this broadcast.
In your XML, put
<activity android:name=".app.DeviceAdminSample"
android:label="#string/activity_sample_device_admin">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
<receiver android:name=".app.DeviceAdminSample$DeviceAdminSampleReceiver"
android:label="#string/sample_device_admin"
android:description="#string/sample_device_admin_description"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin"
android:resource="#xml/device_admin_sample" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
In the receiver, you have at least two methods worth noticing:
#Override
public CharSequence onDisableRequested(Context context, Intent intent) {
…
}
#Override
public void onDisabled(Context context, Intent intent) {
…
}
This way you know the user is potentially going to erase your app.
Complete guide for device administration is at https://developer.android.com/guide/topics/admin/device-admin.html
If you have root permissions make your app system (remove your apk-file from /data to /system directories). Then reboot device. After reboot your app is not available to delete by user (not superuser).
The only way i see, is to provide your own uninstaller as part of your app (= an activity that lists all apps and allows to uninstall them). Your service could then check if your app was the one that started the packageinstaller and if not redirect the user.
It is not possible (at least on the Android 4.4 I tested with) to grab the uninstaller activity data without root or being a system app. This is because the uninstaller is not called as an independent task, but as an activity on the stack of the starting task (which is the Settings app when uninstalling from settings, etc). You can only see the Task details of the calling task.
However there might be some really dirty possibility left, that i didn't test to the end: You could register the hidden interface IThumbnailReceiver [1] with the hidden three argument version of ActivityManager.getRunningTasks [2]. It seems like only the GET_TASKS permission is needed to grab a thumbnail (see [3]). It should be possible to find out which app is going to be removed from the app thumbnail... - But as this solution uses hidden APIs, there is no guarantee that it will work with older/newer/vendored Android versions.
https://github.com/omnirom/android_frameworks_base/blob/android-4.4/core/java/android/app/IThumbnailReceiver.aidl
https://github.com/omnirom/android_frameworks_base/blob/android-4.4/core/java/android/app/ActivityManager.java#L766
https://github.com/omnirom/android_frameworks_base/blob/android-4.4/services/java/com/android/server/am/ActivityManagerService.java#L6725
First of all, I have researched a lot about my issue, but I could not find a proper solution so I am posting my query here. Hope to get a better solution to the issue:
I have a requirement where I need to ask for password to the user before user deletes my app from settings or from any other application like MyAppSharer. I have found one solution where I can successfully be able to call my activity when user clicks on Uninstall button. I have applied trick here, and calling service. In service, I run timer which runs every 1 second and in that one second, it checks for top most activity of running task. This is running perfectly as per expected.
Now, my issue is, this activity apppears on each of application user tries to uninstall. I need that the activity which I call, should only appear for my application when user tries to uninstall my application.
Here is my code:
public static final String PACKAGE_INSTALLER = "com.android.packageinstaller";
public static final String PACKAGE_INSTALLER_UNINSTALL_ACTIVITY = "com.android.packageinstaller.UninstallerActivity";
alarmTimer.scheduleAtFixedRate(new TimerTask() {
public void run() {
mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);;
ComponentName topActivity = mActivityManager.getRunningTasks(1).get(0).topActivity;
final String packageName = topActivity.getPackageName();
String className = topActivity.getClassName();
Log.v(TAG, "packageName:" + packageName);
Log.v(TAG, "className:" + className);
if (PACKAGE_INSTALLER.equals(packageName)
&& PACKAGE_INSTALLER_UNINSTALL_ACTIVITY.equals(className)) {
//Here I need to apply one condition where package name received to be matched with my package name. But I am not sure how to fetch package name of selected application for uninstalling
//To Cancel Existing UninstallerActivity and redirect user to home.
Intent homeIntent = new Intent();
homeIntent.setAction(Intent.ACTION_MAIN);
homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
homeIntent.addCategory(Intent.CATEGORY_HOME);
startActivity(homeIntent);
//To open my activity
Intent loginActivity = new Intent(UninstallService.this, Act_Login.class);
loginActivity.putExtra(Constants.KEY_IS_FROM_SERVICE, true);
loginActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(loginActivity);
}
}
}, 0, 1000);
you should try something like the following :
1st - declare your broadcast recevier in the Manifest file , that will listen to QUERY_PACKAGE_RESTART :
<receiver android:name=".UninstallReceiver">
<intent-filter android:priority="999999">
<action android:name="android.intent.action.QUERY_PACKAGE_RESTART" />
<data android:scheme="package" />
</intent-filter>
</receiver>
2nd - your UnunstallIntentReceiver java class like the following :
public class UninstallReceiver extends BroadcastReceiver{
#Override
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("application_package")){
// start your activity here and ask the user for the password
}
}
}
}
}
and please give me some feedback
Hope That Helps.
If this is a corporate requirement (if you want to block a regular user from uninstalling your app, no chance, thanks Google for protecting us from bad devs), you should create a device administrator application. This way, although the user still can delete the app, it's one extra step if you want to prevent accidental erasing.
Before deleting your app, if it's enabled as device admin, the user must first disable the app as administrator, and the app receives this broadcast.
In your XML, put
<activity android:name=".app.DeviceAdminSample"
android:label="#string/activity_sample_device_admin">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
<receiver android:name=".app.DeviceAdminSample$DeviceAdminSampleReceiver"
android:label="#string/sample_device_admin"
android:description="#string/sample_device_admin_description"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin"
android:resource="#xml/device_admin_sample" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
In the receiver, you have at least two methods worth noticing:
#Override
public CharSequence onDisableRequested(Context context, Intent intent) {
…
}
#Override
public void onDisabled(Context context, Intent intent) {
…
}
This way you know the user is potentially going to erase your app.
Complete guide for device administration is at https://developer.android.com/guide/topics/admin/device-admin.html
If you have root permissions make your app system (remove your apk-file from /data to /system directories). Then reboot device. After reboot your app is not available to delete by user (not superuser).
The only way i see, is to provide your own uninstaller as part of your app (= an activity that lists all apps and allows to uninstall them). Your service could then check if your app was the one that started the packageinstaller and if not redirect the user.
It is not possible (at least on the Android 4.4 I tested with) to grab the uninstaller activity data without root or being a system app. This is because the uninstaller is not called as an independent task, but as an activity on the stack of the starting task (which is the Settings app when uninstalling from settings, etc). You can only see the Task details of the calling task.
However there might be some really dirty possibility left, that i didn't test to the end: You could register the hidden interface IThumbnailReceiver [1] with the hidden three argument version of ActivityManager.getRunningTasks [2]. It seems like only the GET_TASKS permission is needed to grab a thumbnail (see [3]). It should be possible to find out which app is going to be removed from the app thumbnail... - But as this solution uses hidden APIs, there is no guarantee that it will work with older/newer/vendored Android versions.
https://github.com/omnirom/android_frameworks_base/blob/android-4.4/core/java/android/app/IThumbnailReceiver.aidl
https://github.com/omnirom/android_frameworks_base/blob/android-4.4/core/java/android/app/ActivityManager.java#L766
https://github.com/omnirom/android_frameworks_base/blob/android-4.4/services/java/com/android/server/am/ActivityManagerService.java#L6725
Im currently disabling an application via
setApplicationEnabledSetting(String, int, int)
The application is actually disabling itself.
I would expect upon re-installation of the app, the app would re-enable itself, however this isnt the case.
Is there a particular setting required in the manifest to make this work.
(Ive tried setting enabled=true)
Thanks
Im currently disabling all components apart from a broadcast receiver and am trying to catch the installation process to re-enable all the other components again. Which is nasty to say the least
One way to do this is to listen to package installation broadcasts and take action accordingly.
Listen to Intent.ACTION_PACKAGE_ADDED in your broadcast receiver.
If the newly added package is yours, enable the other components.
Example
Manifest:
<receiver android:name =".MyReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED"/>
<data android:scheme="package" />
</intent-filter>
</receiver>
Receiver:
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
final String packageName = intent.getData().getSchemeSpecificPart();
if (replacing && "my.package.name".equals(packageName)) {
// Re-enable the other components
}
}
}
}
Hope this helps.
I work in a company that produces several apps, not all those apps have the same signature or more like it we have at least 5-6 apps certificates for the time being.
We tried to create a mechanism in which all the companie's apps on the same device share the same is,
For example if user installed from the market App A and no app installed, a new ID will be generated, if now he installs App A, app B should have the same id as App A(id is just a generated UUID type #4) etc...
We are using broadcast at the moment and only apps with our permission can receive that broadcast and send back the id with another broadcast(explicit this time).
The broadcast and the responses are protected with our permission with signature level, this is of course not helping since we have more than one signature.
I tried to write an intent broadcast and recover that can have it's own mechanism of protection that will not be limited to only one signature but several, the problem is that things like Binder.getSenderUID() doesn't work for broadcasts and i get my own uid.
it looks like i have no way to get the identity of my snder unless he itself writes his id in the intent, which is NOT something i can trust as it can be easily faked.
Using encryption requires the apps to come with a key on them, which is not secured once more, turning to a server for validation takes too much time and on mobile not guaranteed to success since not 100% sure there is network around.
Anyone has any idea how can one get a validate\secure message from one app to another ?(all my apps but may have different signatures).
As always with a challenging question here i never get a proper, if ANY!' answer, so i'm forced to find it myself.
The problem with Intents is that it's not possible to get the sender as they are the parallel to mulicast in a network where there sender's address is not important.
If i wish to get the snder's UID i need to make a "remote" process even if he is local, instead of using broadcast IPC i need to use AIDL with IBInder implementation.
Once i have a Binder object i can call in my service getCallingUid() and get the uid of the caller, this will allow me to ask PackageManager to give me it's public certificate (without asking the process itself, i ask the OS) and compare it to a set of certificates i prepared in advance in the apk.
The calling application on the other side(the other process that sends me it's ID) just has to use the bindService(service, conn, flags) method to bind to me.
The disadvantage to this approach is ofcourse the time consuming process, Bind takes time, it's an async call that pass through the kernel and is not as fast as binding to a local service. Moreover since i might have several applications i need to synchronize the access my internal ID so only the first binding call that didn't fail will set and ID for me.
I still need to check if i can use the Messanger method that prevents the multi thread issues.
Hopes this helps someone else.
Sorry for late response...
Bind takes time, and more importantly, its asynchronous.
However, there is a way to make a synchronous bind - assuming of course the service you attempt to contact is already started at the time.
Android allowed for this more for BroadcastReceivers (which are async in nature, and thus can't use normal bindService) a BroadcastReceiver has a "peekService" method.
If you want to use it without listening to a broadcast, you can by doing:
final IBinder[] b = new IBinder[1];
new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
b[0] = peekService(context, intent);
}
}.onReceiver(context, intent);
IMyInterface i = IMyInterface.Stub.asInterface(b[0);
note that you don't bind to the service, so make sure to peek at on each use.
As already stated, binding is probably the best solution to this. However, you could consider switching to an Activity rather than a BroadcastReceiver then you can use getCallingActivity(), assuming you launched with startActivityForResult().
Declare you Activity as follows to make it "silent" like a BroadcastReceiver:
<activity
android:name=".FauxReceiver"
android:theme="#android:style/Theme.NoDisplay"
android:excludeFromRecents="true"
android:noHistory="true"
>
<intent-filter>
...
</intent-filter>
</activity>
Inspiration: How to get the sender of an Intent?
I was looking for a way to verify the package name of the application that sent the intent received by my intent-filter. That activity in my app that handles the intent-filter requires that the intent sender includes their process id in an Intent Extras field. My receiving activity can then get the associated application package name from the ActivityManager.
Here is some example code I found while shifting through StackOverflow.
Constants needed for both Apps
public static final String EXTRA_APP_ID;
public static final String ACTION_VERIFY = "com.example.receivingapp.action.VERIFY";
Calling Activity
Intent verifyIntent = new Intent();
verifyIntent.setAction(Consts.ACTION_VERIFY);
verifyIntent.putExtra(EXTRA_APP_ID, android.os.Process.myPid());
// Verify that the intent will resolve to an activity
if (verifyIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(verifyIntent, Consts.REQUEST_VERIFY);
} else {
Log.d(TAG, "Application not found.");
}
Receiving App
Manifest
<activity
android:name="com.example.receivingapp.ReceivingActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="com.example.receivingapp.VERIFY" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
ReceivingActivity
if (getIntent().hasExtra(OnyxCoreConsts.EXTRA_APP_ID)) {
string appName = null;
// Resolve intent
if (getIntent().getAction().equals(ACTION_VERIFY) {
int appPid = getIntent().getIntExtra(EXTRA_APP_ID, -1);
if (-1 != mAppPid) {
appName = Utils.getAppNameByPID(mContext, mAppPid);
}
if (null != appName && !"".equalsIgnoreCase(appName)) {
// Do something with the application package name
}
}
}
Utils class
public static String getAppNameByPID(Context context, int pid){
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
if (processInfo.pid == pid) {
return processInfo.processName;
}
}
return "";
}