I am working at an OEM and would like to know how to deactivate the "Disable option" so that our device user can't remove pre loaded App, which is a security App. Is there an AndroidManifest Attribute in SDK 4.0. If so please let me know.
Thank you much.
No there is not a way to prevent the user from disabling pre-installed apps, and that is very much by design. The philosophy for this is that if disabling an app will not prevent the user from being able to get to settings and re-enable it, then they should be allowed to disable it.
I checked the sourcecode of the Settings app which contains the following function.
The SUPPORT_DISABLE_APPS is a systemwide flag. If you want to prevent any(!) app from beeing disabled, you can set this flag to false.
In short, the comment tells everything: The only apps which can not be disabled are:
(home screen-) launchers. These are identified by implementing a Intent with action ACTION_MAIN and category CATEGORY_HOME.
apps signed with the system cert. As you can modifiy and create this certificate during the build-process of the system image, it should be possible to sign your application with this key and thus prevent the disableing.
if (SUPPORT_DISABLE_APPS) {
try {
// Try to prevent the user from bricking their phone
// by not allowing disabling of apps signed with the
// system cert and any launcher app in the system.
PackageInfo sys = mPm.getPackageInfo("android",
PackageManager.GET_SIGNATURES);
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setPackage(mAppEntry.info.packageName);
List<ResolveInfo> homes = mPm.queryIntentActivities(intent, 0);
if ((homes != null && homes.size() > 0) ||
(mPackageInfo != null && mPackageInfo.signatures != null &&
sys.signatures[0].equals(mPackageInfo.signatures[0]))) {
// Disable button for core system applications.
mUninstallButton.setText(R.string.disable_text);
} else if (mAppEntry.info.enabled) {
mUninstallButton.setText(R.string.disable_text);
enabled = true;
} else {
mUninstallButton.setText(R.string.enable_text);
enabled = true;
}
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Unable to get package info", e);
}
There is nothing in the SDK for this. However, an app you put in the firmware cannot be uninstalled by the user, unless they root their device.
Related
I'm looking for a way to open the battery settings screen from an android app.
So far I found the two intents :
Intent.ACTION_POWER_USAGE_SUMMARY
Settings.ACTION_BATTERY_SAVER_SETTINGS
but none of them open this screen.
I was wondering if anyone knows of such a way. It sounds strange that an intent for something so simple doesn't exist
Settings.ACTION_BATTERY_SAVER_SETTINGS on "plain" Android versions will show the settings page you want to show.
Intent.ACTION_POWER_USAGE_SUMMARY will lead to the overview page showing the battery consumption.
Some manufactures such as Samsung build their own implementation over the system one, e.g. in this the "Battery" page. On Samsung devices, you can call this by calling the SmartManager interface directly. An code example:
if (Build.MANUFACTURER == "samsung") {
val intent = Intent()
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
intent.component = ComponentName("com.samsung.android.lool", "com.samsung.android.sm.ui.battery.BatteryActivity")
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
intent.component = ComponentName("com.samsung.android.sm", "com.samsung.android.sm.ui.battery.BatteryActivity")
}
try {
activity?.startActivity(intent);
} catch (ex: ActivityNotFoundException) {
// Fallback to global settings
startActivity(Intent(Settings.ACTION_SETTINGS))
}
} else {
startActivity(Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS))
}
It can be the case that you need additional cases for Huawei or Xiaomi as well.
Huawei can be "com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity"...
...and the MIU based ones "com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity"
I know this is quite old. But a trick I use is going to the appropriate settings screen in the device settings and then while connected to the phone run:
adb shell
dumpsys window windows | grep -E 'mCurrentFocus'
This returns the package name and Activity name currently in focus.
Using that I can check in code if the intent is callable. If it is, I launch it. If it isnt, I might have better luck with a different screen that is near by or explain to the user he needs to do something manually etc... Obviously the more devices you have, the more Intents you can create and check at run time. Im sure there is a list of Intents for different devices online.
All,
I am having a very strange issue.
I have the following code that enables and disables the PIN based on certain conditions
DevicePolicyManager mDPM;
ComponentName mDeviceAdminSample;
mDPM = (DevicePolicyManager)context.getSystemService(Context.DEVICE_POLICY_SERVICE);
mDeviceAdminSample = new ComponentName(context, DeviceAdminSampleReceiver.class);
public void disablePINLock(DBHelper myDBHelper, Context context) {
mDPM.setPasswordQuality(mDeviceAdminSample,
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
mDPM.setPasswordMinimumLength(mDeviceAdminSample, 0);
boolean result = false;
result = mDPM.resetPassword("", DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY);
if (result) {
// Toast: "Successfully Disabled PIN lock"
}
else {
// Toast: "Could not disable PIN lock"
}
}
public void enablePINLock(DBHelper myDBHelper, Context context) {
mDPM.setPasswordQuality(mDeviceAdminSample,
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
mDPM.setPasswordMinimumLength(mDeviceAdminSample, 4);
Cursor c = myDBHelper.getSetting("'random'");
if (c != null) {
boolean result = mDPM.resetPassword("1234",
DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY);
if (result) {
// Toast: "Successfully Enabled PIN lock"
}
else {
// Toast: "Could not enable PIN lock"
}
}
else {
// Toast: "Could not enable PIN lock"
c.close();
}
}
The code was working fine until a few days ago. I restarted the phone to finish updating and since then the disablePINLock function is not working correctly. When the function executes all I get is
"result" is false and toast message "Could not disable PIN". The enablePINLock method is working just fine and has always worked fine.
Since the restart result = mDPM.resetPassword("", DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY); has failed to execute. I tried to catch an exception but it didn't help.
At this point I am clueless as to why the method stopped working after the restart. I checked the device administrators, enabled/disabled the app few times without any success.
Also I am not sure if this is related, but let me say this. The device administrator screen has my app always checked, even after I uncheck the app and deactivate it, the app name has the box checked.
Thank you in advance for any response.
You say you've "checked the device administrators" but you don't say what you saw there. Are there any other active device administrators on the device? DevicePolicyManager.resetPassword() will return false if the password you provide is not compliant with the aggregate of password policies set by all device admins. It sounds like perhaps another device admin is still active.
Also, with regard to your last point:
The device administrator screen has my app always checked, even after I uncheck the app and deactivate it, the app name has the box checked.
That's a common bug with the Device Administrators screen: the checkbox often stays on even when you've successfully disabled an admin. Try backing out of the Device Administrators screen then going back in. It should be unchecked now.
My android application will be preinstalled. And I want to keep tracking of preinstalled apps.
For this purpose I need somehow to save a key or a flag (which means that app is preinstalled). I will add this key to each request to my back-end and will analyze it.
I have an issue with that. An issue is about update from Google Play.
The standart workflow is the following:
1) I give to a manufacturer a special version of my application, which saves a key somehow (in Shared Prefs for example).
2) Manufacturer sell device with the app (special, modified).
3) When User get it, there definetly be next version of the app (standart, without special code) in the Google Play, so user perhaps update it without any launching (the worst case).
4) I lost my tracking possibility. (new apk fully removing never launched old one which was special)
To solve it I was listening a system broadcast ON_BOOT_COMPLETE, but its not working properly on Android 3.1+.
Have you any ideas how can I do that?
Can you install an additional .apk that only has a service? Then that service can have the key, etc. and it can listen for when your app starts and send the tracking info. Then it won't matter if your app gets upgraded; the service will still be the same.
There are some ways to know if application is system application or not. Like by checking installed directory of application or check FLAG_SYSTEM for the application.
Method 1 : -
Check location of application
public static boolean applicationIsSystemApp(Context mContext, String packageName) {
try {
ApplicationInfo applicationInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
String appLocation = applicationInfo.publicSourceDir;
// OR String appLocation = applicationInfo.sourceDir;
// Both returns the same
// if package is pre-installed then output will be /system/app/application_name.apk
// if package is installed by user then output will be /data/app/application_name.apk
// Check if package is system app
if (appLocation != null && appLocation.startsWith("/system/app/")) {
return true;
}
} catch (NameNotFoundException e) {
e.printStackTrace(); // TODO Can handle as your logic
}
return false;
}
Method 2 : -
Check FLAG_SYSTEM of application
public static boolean applicationIsSystemApp(Context mContext, String packageName) {
try {
ApplicationInfo applicationInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
// FLAG_SYSTEM is only set to system applications,
// this will work even if application is installed in external storage
// Check if package is system app
if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
return true;
}
} catch (NameNotFoundException e) {
e.printStackTrace(); // TODO Can handle as your logic
}
return false;
}
And call this method as
if (applicationIsSystemApp(getApplicationContext(), "com.example.mysystemapp")) {
// Application is system app
} else {
// Application has been installed as 3rd Party app
}
There is a property sourceDir in ApplicationInfo class. You can use it to distinguish the system app version and the upgraded one.
System app will start with "/system/app" and upgraded app will start with "/data/app"
Try something like this
try {
ApplicationInfo appInfo = this.getPackageManager().getApplicationInfo("com.example.san", 0);
boolean isSystemApp = false;
if(appInfo.sourceDir.startsWith("/system/app")){ // You can use "contains" too
isSystemApp = true;
}
} catch (Exception e) {
e.printStackTrace();
}
Note: I didnt test it.. Hope it works
One proven solution is to pre-install an APK that on have the permission: RECEIVE_BOOT_COMPLETED
Then on the very first boot - you make a quick notation that you are a PREINSTALLED to whatever persistent storage you use, preferably add a token file.
You look for this token file on all your later APK versions to determine if the running copy originates from a device which had it pre-installed or not.
This solves the mayor issues:
1) Then its OK if the user updates you APK to the latest version, you can still read this token.
2) You don't have to maintain a separate APK on google play for the pre-installed community
3) You don't have to hustle with the OEM to install multiple APK when you actually only have one App.
The issue is that I need to install an apk(non market app) and for this, the user need to activate the unknown source setting, so i send him (if he didn't have it activated) to the settings so he can turn on the option, the issue is that i tested it in different phones and in samsung that option is on applications while in htcs phones is on security. i want send the user to that option but i don't know how to do it
I read about this and no one knows exactly how to do it
this is my code
int canInstallFromOtherSources = Settings.Secure.getInt(ctx2,Settings.Secure.INSTALL_NON_MARKET_APPS);
if(canInstallFromOtherSources == 0)
{
Intent intentSettings = new Intent();
intentSettings.setAction(android.provider.Settings.ACTION_APPLICATION_SETTINGS);
startActivity(intentSettings);
}
You can do it with the following line (changing to the corresponding action):
startActivityForResult(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS), REQUEST_CODE_ENABLE_LOCATION_PROVIDERS);
Check Android Settings documentation.
I think you should use ACTION_SECURITY_SETTINGS and one of ACTION_APPLICATION_SETTINGS or ACTION_APPLICATION_DEVELOPMENT_SETTINGS.
And here (line 304), you've got a working example of one of my apps: Tureame
This question already has answers here:
Way to protect from Lucky Patcher / play licensing [closed]
(8 answers)
Closed 5 years ago.
I know this topic has been opened multiple times and I learnt a lot but I stumbled across a problem I really need advice on.
I'm using LVL with Obfuscation. I changed the default LVL ALOT so that anti-LVL does not break it. However, Lucky Patcher with one click breaks it! I tried to see the new broken APK. Yes it simply called my "allow method".
My question is if someone can recommend a way to prevent Lucky Patcher from breaking it? I know I can't make it bullet-proof, but I want it at least to be not so easy for one-click software.
Code to check your certificate:
public void checkSignature(final Context context) {
try {
Signature[] signatures = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES).signatures;
if (signatures[0].toCharsString() != <YOUR CERTIFICATE STRING GOES HERE>) {
// Kill the process without warning. If someone changed the certificate
// is better not to give a hint about why the app stopped working
android.os.Process.killProcess(android.os.Process.myPid());
}
}
catch (NameNotFoundException ex) {
// Must never fail, so if it does, means someone played with the apk, so kill the process
android.os.Process.killProcess(android.os.Process.myPid());
}
}
Next how to find which one is your certificate. You must produce an APK, in release mode, as the debug certificate is different from the release one. Output your certificate into your Logcat:
signatures[0].toCharsString();
Remember that when you are back to debug mode, the certificate is different again. To avoid debug issues use next line to skip the verification:
if ((context.getApplicationContext().getApplicationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE) != 0)
return;
Next the lucky patcher checker.
I decompiled all versions of Lucky Patcher, and I've found that its creator used 2 package names between all realeases. So you only need to keep track of new versions and keep adding future package names.
private boolean checkLuckyPatcher() {
if (packageExists("com.dimonvideo.luckypatcher"))
return true;
if (packageExists("com.chelpus.lackypatch"))
return true;
if (packageExists("com.android.vending.billing.InAppBillingService.LACK"))
return true;
return false;
}
private boolean packageExists(final String packageName) {
try {
ApplicationInfo info = this.getPackageManager().getApplicationInfo(packageName, 0);
if (info == null) {
// No need really to test for null, if the package does not
// exist it will really rise an exception. but in case Google
// changes the API in the future lets be safe and test it
return false;
}
return true;
}
catch (Exception ex) {
// If we get here only means the Package does not exist
}
return false;
}
As of current version (6.4.6), Lucky Patcher generates very short token. For example, real purchase token:
felihnbdiljiajicjhdpcgbb.AO-J1OyQgD6gEBTUHhduDpATg3hLkTYSWyVZUvFwe4KzT3r-O7o5kdt_PbG7sSUuoC1l6dtqsYZW0ZuoEkVUOq5TMi8LO1MvDwdx5Kr7vIHCVBDcjCl3CKP4UigtKmXotCUd6znJ0KfW
And that is Lucky Token:
kvfmqjhewuojbsfiwqngqqmc
Pretty straight forward solution is to check string length of token
#Override public void onIabPurchaseFinished(IabResult result, Purchase info) {
if (info.getToken().length < 25) {
Log.wtf("PIRATE", "PIRATE DETECTED");
return;
}
}
Implement a function that gets called under certain actions, and which checks whether the LuckyPatcher package is installed in the device.
If found, then exit your app. Don’t allow to use it regardless if is paid or not, better bad reviews than thousands of illegal copies. Alternatively you could show a message stating that LuckyPatcher has been found and the app can't run.
If your app gets patched by LuckyPatcher, meaning that it has hacked your LVL implementation, then at least your app will not execute due to the LuckyPatcher package detection.
A way, is to check if lucky patcher is installed and if so, then show a message to the user, and kill your process afterwards. If a user has it, means he is trying to crack your software or other developer's one. So better not to allow to use your app in a phone that has it installed. Fight piracy.
Whenever Lucky Patcher creates a modded APK file, it always ends up with a different package name, as you can't run two apps under the same package name.
Here's a simple solution that checks if your code is running under the wrong package name:
PackageManager pm = getPackageManager();
try {
PackageInfo packageInfo = pm.getPackageInfo("YOUR_PACKAGE_NAME",PackageManager.GET_ACTIVITIES);
} catch (PackageManager.NameNotFoundException e){
finish();
//If you get here, your code is running under a different package name... Kill the process!
}
I just call finish(); on my app and I can't break it, but it might be best to use android.os.Process.killProcess(android.os.Process.myPid()); as #PerracoLabs suggested.