I want to find applications that the user has allowed to install other applications.
I know that if I look for "android.permission.INSTALL_PACKAGES" I would find mainly system packages that can install applications but I'd like to find applications (like the browser, or email) that the user allowed to install applications.
I'm trying the following code to loop through all applications which is obviously wrong as I can not find Chrome, who I allowed to install applications. Any suggestion how to achieve this?
final PackageManager pm = getPackageManager();
for (final PackageInfo pi : pm.getInstalledPackages(GET_PERMISSIONS)) {
try {
Context pcontext = createPackageContext(pi.packageName, 0);
if (ContextCompat.checkSelfPermission(pcontext, Manifest.permission.INSTALL_PACKAGES) == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Application able to install other apps: " + pi.packageName);
}
}catch (SecurityException|PackageManager.NameNotFoundException ex){
Log.d(TAG, "Exception " + ex);
}
}
If I'm understanding your question correctly, the answer is "that's not allowed."
Third-party apps can't actually install applications (see https://developer.android.com/reference/android/Manifest.permission#INSTALL_PACKAGES). Apps like Chrome call the system installer, they don't get install privileges themselves, so that's why you don't see them on the list.
And there's no way to check which other apps have launched an installer process. That would be a security/privacy issue, as it would mean any app could retrieve a list of other apps a user has installed on their device! Each process is assigned its own linux id and can't directly access data for any other app (i.e. it's sandboxed), using the exact same mechanism, in fact, that prevents one user from querying another user's data on a standard linux system.
Related
I am trying to customize Android for a media device. We got the firmware from the manufacturer and it's based on Android 7 with minor modifications.
One of the things we would like to do is to restrict installation of apps on the device to certain apps only. We won't install any app store like Google Play on the device. We will build the firmware and install all apps onto the devices at our workshops and deliver to the customers. In future, we may want to install more apps on the devices via OTA or some mechanisms.
We would like to disallow customers sideloading other apps via USB port. We created a file (eg., whitelisted_apps.txt) that has the list of all approved app names, such as -
com.mycompany.android.app1
com.mycompany.android.app2
com.ourpartner.android.appx
We tweaked the PackageInstaller app of AOSP so that when a *.APK file is opened via the file browser and when the methods in PackageInstallerActivity.java are called, it will compare the name of the app to be installed against those in whitelisted_apps.txt and if the name is not found, disallow installation of the new app. It's working.
Now, we want to improve it further because whitelisted_apps.txt can be manipulated. Some people suggest using sqlite to keep the list. We are not sure if it will be the best solution.
Some suggest using certificates and signing and we think it's a better solution than others. We will sign all the apps we want to install on the device with our key. When a *.APK file is sideloaded, the PackageInstaller will get the signature of the APK and compare against ours. If they match, the app can be sideloaded.
We followed this excellent resource: SignatureCheck.java. It's working with the hardcoded APP_SIGNATURE. We have this currently:
public boolean validateAppSignature() throws NameNotFoundException {
boolean sigMatch = false;
String APP_SIGNATURE = "123456784658923C4192E61B16999";
PackageInfo packageInfo3 = mPm.getPackageInfo(
getPackageName(), PackageManager.GET_SIGNATURES);
try {
for (Signature signature : packageInfo3.signatures) {
// SHA1 the signature
String sha1 = getSHA1(signature.toByteArray());
Log.i(TAG, "got this SHA1 of the signature ... "+sha1);
// check if it matches hardcoded value
sigMatch = APP_SIGNATURE.equals(sha1);
if (sigMatch){
return sigMatch;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
Just that we don't know how to do that in a real world situation. When we build, install apps or release with OTA, we sign those apps with our private key and then on the device (PackageInstaller app), we hardcode our certificate's SHA1 signature as APP_SIGNATURE so that we can compare? Any other ideas?
Thank you so much.
Seems like you're trying way too hard to partially shut down sideloading. Use a Device Owner to completely turn off installation, and just preload the apps you want. Or make it so the only app that can install is your own downloader that only looks at your server.
I'm trying to distinguish between system applications and applications installed by user
with the following piece of code:
public void getInstalledApps() {
int flags = PackageManager.GET_META_DATA |
PackageManager.GET_SHARED_LIBRARY_FILES |
PackageManager.GET_UNINSTALLED_PACKAGES;
PackageManager pm = context.getPackageManager();
List<ApplicationInfo> applications = pm.getInstalledApplications(flags);
for(ApplicationInfo appInfo : applications) {
if((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 1) {
// System application
Log.i("TESTAPPSYSTEM", pm.getApplicationLabel(appInfo).toString());
} else {
// Installed by user
Log.i("TESTAPPUSER", pm.getApplicationLabel(appInfo).toString());
}
}
}
But in Android Studio's console i saw that:
06-17 15:19:42.639 14822-14822/it.example.myapplication.app I/TESTAPPSYSTEM WhatsApp
On the contrary of others applications installed by user (like Telegram), Whatsapp is seen as a system application, not as one installed by user: why?
Whatsapp is pre-installed either with your Android operating system from the manufacturer or by a ROM you have applied to your phone, you can not change this, however if you have a rooted phone then you can uninstall WhatsApp then install it from the play store so that it is installed as a user application not a system application. Furthermore there are apps out there which close system apps, although once again you would need to have root access to perform this task I believe.
I want to prevent launching of task manager and Settings applications in my application. For this, I tried to obtain currently running application and checked whether their package name is allowed or not .If it is not allowed then show a new activity.
When work out it is show that the package name of default android Settings application is com.android.settings. Now I have some doubts
Is the Settings application has package name com.android.settings in all android versions? If not, which are they?
How to find package name of Task Manager?
try this
private String querySettingPkgName() {
Intent intent = new Intent(android.provider.Settings.ACTION_SETTINGS);
List<ResolveInfo> resolveInfos = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (resolveInfos == null || resolveInfos.size() == 0) {
return "";
}
return resolveInfos.get(0).activityInfo.packageName;
}
For this,I tried to obtain currently running application and checked whether their package name is allowed or not .If it is not allowed then show a new activity.
Fortunately, for the users affected by your app, this will be unreliable.
Is the Settings application has package name com.android.settings in all android versions?
Not necessarily. More importantly, any given firmware can have any number of applications that modify settings, supplied by the firmware author. Some settings can be modified even without being part of the firmware, particularly on rooted devices.
If not,which are they?
You are welcome to make a list of all device manufacturers and ROM mod authors and ask them that question.
How to find package name of Task Manager?
There are any number of "task manager" apps included in devices, ROM mods, and available on the Play Store and other distribution points. You are welcome to make a list of all of them and ask their authors that question.
shell into the device using adb, and invoke:
pm list packages
this will provide you a list of pacakges. from there you will should see:
com.android.settings
final PackageManager pm = getPackageManager();
List<ApplicationInfo> packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo packageInfo : packages) {
Log.d("Packages", "" + packageInfo.packageName);
}
above code should help you
It's not totally clear what is the scenario.
I guess it is something along the lines of showing off devices to public but not have them f'up the device for others.
Maybe it would be better to do a whitelist instead of a blacklist. Meaning the shop should state which apps should be testable on the devices and then you start your activity if it is any other.
But this again will need maintenance: package names of popular apps may also change. You better provide a way of updating the settings of your app via an online service so you can change the needed packages without physical access to the devices and without having to download and install the complete app.
If you just need a device that goes through many hands and should not be tempered with I suggest using a modified device. I only know of Sonim: they provide a library (needs a Sonim provided hash key in your manifest to use that). With it you can prohibit the altering of many settings without preventing access to the whole settings app.
Not all phones have Android Market installed, and therefore using intent to open market app fails.
What's the best way to handle this?
Hide this feature if user doesn't have Android Market installed (how would I detect this?).
Handle the possible error, how (and possibly suggest that the user downloads the Android Market)?
The problem with the answer above is that if you just pass a URL the user will be prompted how to handle the Intent.
A more graceful way to do it IMO, expanding upon the 1st answer above, is to test whether the market app is installed, and then if not, pass a URL (which actually you would then want to test to see if something can handle that intent, but if you happen to have a device without both the play store and a browser then I would question why the user would have my app installed in the first place (another story I suppose)....
Perhaps there is a better way, but here's what works for me:
private String getMarketURI(String marketURL) {
String returnURL = "";
PackageManager packageManager = getApplication().getPackageManager();
Uri marketUri = Uri.parse("market://" + marketURL);
Intent marketIntent = new Intent(Intent.ACTION_VIEW).setData(marketUri);
if (marketIntent.resolveActivity(packageManager) != null) {
returnURL = "market://" + marketURL;
} else {
returnURL = "https://play.google.com/store/apps/" + marketURL;
}
return returnURL;
}
And then use it like so:
marketIntent.setData(Uri.parse(getMarketURI("details?id=com.yourapps.packagename")));
If your app is being provided by Android Market, then it does have Android Market installed. :)
Okay that is snide, but there is an important truth -- Google goes to a lot of effort to enforce compatibility guarantees on devices for them to be allowed to ship with Android Market, so that is how you can know that whatever you are running on will behave as it should.
If you are delivering your app from something besides Android Market, you need to get information from whoever is delivering the app about what compatibility guarantees they have.
If they don't have compatibility guarantees (or you are just putting a raw .apk up on a web site or such), then you have a complete crap shoot. The device you are running on could have had its software modified in pretty much any way, and have any kind of differences in behavior you can imagine.
That said, if you want to determine whether there is an activity on the current device to handle a particular Intent, you can use this: PackageManager.resolveActivity
Use the web address as the intent target and then if there is no android market it will open in a browser.
I'm developing a non-public Android app, i.e. the app won't be available in the global Android Market. The app will be installed on a limited number of clients, e.g. by using an apk file.
How can I enable an auto-update functionality in this app?
I see different potential options (I do not know if those are technically hard or even impossible to implement or if there are any existing functionalities that can be reused):
On each launch the app tests if a new version exists (by requesting a server), if so downloads the new apk and replaces itself with the new version.
Use (or develop?) a separated app or service that undertakes the update-check and replacement-process.
Use (or develop?) a private market app which has an auto-update option. This option is similar to the second one, but more generic: The market app would be connected to a repository, i.e. it would handle an arbitrary number of (private) apps.
I would prefer option one since the auto-update functionality is included in the app which needs less development efforts.
janjonas, in the company I work we had a similar problem with Windows Mobile 6.x, and we use pretty much the same solution pointed by EboMike:
The main app check if it's updated, against a WebService. It receives the current version & the URL from where download the new version, if necessary. The main app then start the Updater app, passing the URL, and quit.
The Updater do the download of the new program, via HTTP, showing to the user the % downloaded. The user can cancel the download anytime, in a controlled way, and the Updater can registry this cancellation.
Since the new app is downloaded, the Updater run the new app, and quit.
I think option one is the least amount of work for you, and actually the cleanest one too since it will go through the proper channel of using Android's built-in package installer which includes user notification and the option for the user to abort the installation if desired.
You already have it all outlined - check for a new version on a server (would be nice to give the user the option to turn that off), and if there is a new version, you could either just link to the URL with the APK (which will, IIRC, use the browser's download manager to download it), or you could download it with your app and then point the intent to your local file. Using the HTTP link is technically less work and cleaner - the more you let the operating system do, the better - unless there's a reason not to.
Enabling "Install non-market app" is still needed for any application outside the Google Play. If it not enabled, the installation process is going to ask for it and redirect the user to the Application Settings, and after that, the user can install the app.
Depending on your needs, you can delegate to a third part lib.
Some of the permissions we'll use to get this done are the following:
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Let me explain a bit... The last, WRITE_EXTERNAL_STORAGE, is self-explanatory. With ACCESS_SUPERUSER we'll tell the system that we intend to use root privileges. READ_EXTERNAL_STORAGE will be needed in the future in order for your app to read files on SD card.
Assuming that you have downloaded the file and that all those devices can be rooted (limited number of clients, not on Play, etc.), you could do this:
String filePath = Environment.getExternalStorageDirectory().toString() + "/your_app_directory/your_app_filename.apk";
Process installProcess = null;
int installResult = -1337;
try {
installProcess = Runtime.getRuntime().exec("su -c pm install -r " + filePath);
} catch (IOException e) {
// Handle IOException the way you like.
}
if (installProcess != null) {
try {
installResult = installProcess.waitFor();
} catch(InterruptedException e) {
// Handle InterruptedException the way you like.
}
if (installResult == 0) {
// Success!
} else {
// Failure. :-/
}
} else {
// Failure 2. :-(
}
Here might be a very lame method but for some companies, if you believe its applicable, this might be very easy to implement.
Create an password screen (passwordActivity) that asks a password to access the application.
Once the password is entered, raise a flag (set a boolean value from false to true using sharedpreferences)
Place the .apk file on Google Store.
Change the password once everyone installs the app, and release a new update on Google Play Store.
Since the software is going to cache the flag value, the password screen won`t show up even the password is change. It will only show up for new installations so might need to repeat the process.
Note: This method might better fit if there is not hundreds of users using the application. And don`t forget this method is also not secure. To sum up, if you are looking a way to keep the application private and have no security concerns, this is what I recommend.
Update app
Make sure that you already have your new apk download on location
void installNewVersion(String location) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(location + "app-debug.apk")),
"application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}