How can I detect the device run under Xiomi's MIUI ROM? I'm able to detect Xiomi device with the following code.
String manufacturer = "xiaomi";
if (manufacturer.equalsIgnoreCase(android.os.Build.MANUFACTURER)) {
}
But how can I detect its MIUI?
Get device properties:
adb shell getprop
should result with:
[ro.miui.cust_variant]: [x]
[ro.miui.has_cust_partition]: [x]
[ro.miui.has_handy_mode_sf]: [x]
[ro.miui.has_real_blur]: [x]
[ro.miui.mcc]: [xxx]
[ro.miui.mnc]: [xxx]
[ro.miui.region]: [x]
[ro.miui.ui.version.code]: [x]
[ro.miui.ui.version.name]: [x]
[ro.miui.version.code_time]: [xxx]
And a few more consisting MIUI specific properties
Class<?> c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class);
String miui = (String) get.invoke(c, "ro.miui.ui.version.code"); // maybe this one or any other
// if string miui is not empty, bingo
Or, get list of packages:
adb shell pm list packages
should result with
package:com.miui.system
package:com.android.calendar
package:com.miui.translation.kingsoft
package:com.miui.virtualsim
package:com.miui.compass
...
So you could check with this piece of code:
//installedPackages - list them through package manager
for (String packageName : installedPackages) {
if (packageName.startsWith("com.miui.")) {
return true;
}
}
private static boolean isIntentResolved(Context ctx, Intent intent ){
return (intent!=null && ctx.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null);
}
public static boolean isMIUI(Context ctx) {
return isIntentResolved(ctx, new Intent("miui.intent.action.OP_AUTO_START").addCategory(Intent.CATEGORY_DEFAULT))
|| isIntentResolved(ctx, new Intent().setComponent(new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity")))
|| isIntentResolved(ctx, new Intent("miui.intent.action.POWER_HIDE_MODE_APP_LIST").addCategory(Intent.CATEGORY_DEFAULT))
|| isIntentResolved(ctx, new Intent().setComponent(new ComponentName("com.miui.securitycenter", "com.miui.powercenter.PowerSettings")));
}
Itents list from taken from https://github.com/dirkam/backgroundable-android
Detect android device info, MIUI version :
public static boolean isMiUi() {
return !TextUtils.isEmpty(getSystemProperty("ro.miui.ui.version.name"));
}
public static String getSystemProperty(String propName) {
String line;
BufferedReader input = null;
try {
java.lang.Process p = Runtime.getRuntime().exec("getprop " + propName);
input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
line = input.readLine();
input.close();
} catch (IOException ex) {
return null;
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return line;
}
Credits goes to Muyangmin
Related
I'm loading all apps from android and then I display them in my launcher (icon + name). Code looks like following:
public class PhoneAppItem
{
String mPackageName = null;
String mActivityName = null;
String mName = null;
ActivityInfo mActivityInfo = null;
public PhoneAppItem(String packageName, String activityName)
{
mPackageName = packageName;
mActivityName = activityName;
}
public void loadInfo()
{
PackageManager pm = MainApp.get().getPackageManager();
try
{
// following line throws the exception!
mActivityInfo = pm.getActivityInfo(new ComponentName(mPackageName, mActivityName), 0);
// some other code...
}
catch (PackageManager.NameNotFoundException e)
{
L.e(e);
mName = mPackageName;
}
catch (NullPointerException e)
{
L.e(e);
mName = mPackageName;
}
}
}
Package names and activity names for all my PhoneAppItem items are retrieved like following (for android < 5 which is relevant for the only yet known device having this issue):
List<PhoneAppItem> apps = new ArrayList<>();
ActivityManager activityManager = (ActivityManager) MainApp.get().getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> processInfos = activityManager.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo processInfo : processInfos)
{
try
{
ApplicationInfo ai = pm.getApplicationInfo(processInfo.processName, 0);
if (!excludedPackages.contains(ai.packageName))
apps.add(new PhoneAppItem(ai.packageName, ai.className));
}
catch (PackageManager.NameNotFoundException e) {}
}
I now have a user that get's following exception:
android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{com.package.myapp/com.package.myapp.app.MainApp}
at android.app.ApplicationPackageManager.getActivityInfo(ApplicationPackageManager.java:262)
at com.package.myap.classes.PhoneAppItem.h(PhoneAppItem.java:70)
...
Observations
the exception above is thrown for all apps, even for my own app
until now, this behaviour is unique to one user using following hardware: Lenovo Yoga Tablet HD+ (b8080), Android 4.4.2
Question
Does anyone have an idea why this could happen?
If processInfo.processName does not work, try another method of getting package:
processInfo.pkgList[0]
I'm developing an app that gets the MAC address of the device, but since Android 6.0 my code doesn't work, giving me an incorrect value.
Here's my code...
public String ObtenMAC()
{
WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
WifiInfo info = manager.getConnectionInfo();
return(info.getMacAddress().toUpperCase());
}
Instead of the real MAC address, it returns an strange code: 02:00:00:00:00:00.
Please refer to Android 6.0 Changes.
To provide users with greater data protection, starting in this release, Android removes programmatic access to the device’s local hardware identifier for apps using the Wi-Fi and Bluetooth APIs. The WifiInfo.getMacAddress() and the BluetoothAdapter.getAddress() methods now return a constant value of 02:00:00:00:00:00.
To access the hardware identifiers of nearby external devices via Bluetooth and Wi-Fi scans, your app must now have the ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permissions.
Use below code to get Mac address in Android 6.0
public static String getMacAddr() {
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (!nif.getName().equalsIgnoreCase("wlan0")) continue;
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return "";
}
StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
res1.append(Integer.toHexString(b & 0xFF) + ":");
}
if (res1.length() > 0) {
res1.deleteCharAt(res1.length() - 1);
}
return res1.toString();
}
} catch (Exception ex) {
//handle exception
}
return "";
}
I didn't get the above answer to work, but stumbled upon another answer.
Here is a complete and simple method on getting the IPv6 address and then getting the mac address from it.
How to get Wi-Fi Mac address in Android Marshmallow
public static String getMacAddr() {
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (!nif.getName().equalsIgnoreCase("wlan0")) continue;
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return "";
}
StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
res1.append(String.format("%02X:",b));
}
if (res1.length() > 0) {
res1.deleteCharAt(res1.length() - 1);
}
return res1.toString();
}
} catch (Exception ex) {
}
return "02:00:00:00:00:00";
}
Tested it already and it works. Many thanks to Rob Anderson!
this is the complete 2 ways code of getting it successfully on Marshmallow , just copy past this and it will work !
//Android 6.0 : Access to mac address from WifiManager forbidden
private static final String marshmallowMacAddress = "02:00:00:00:00:00";
private static final String fileAddressMac = "/sys/class/net/wlan0/address";
public static String recupAdresseMAC(WifiManager wifiMan) {
WifiInfo wifiInf = wifiMan.getConnectionInfo();
if(wifiInf.getMacAddress().equals(marshmallowMacAddress)){
String ret = null;
try {
ret= getAdressMacByInterface();
if (ret != null){
return ret;
} else {
ret = getAddressMacByFile(wifiMan);
return ret;
}
} catch (IOException e) {
Log.e("MobileAccess", "Erreur lecture propriete Adresse MAC");
} catch (Exception e) {
Log.e("MobileAcces", "Erreur lecture propriete Adresse MAC ");
}
} else{
return wifiInf.getMacAddress();
}
return marshmallowMacAddress;
}
private static String getAdressMacByInterface(){
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (nif.getName().equalsIgnoreCase("wlan0")) {
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return "";
}
StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
res1.append(String.format("%02X:",b));
}
if (res1.length() > 0) {
res1.deleteCharAt(res1.length() - 1);
}
return res1.toString();
}
}
} catch (Exception e) {
Log.e("MobileAcces", "Erreur lecture propriete Adresse MAC ");
}
return null;
}
private static String getAddressMacByFile(WifiManager wifiMan) throws Exception {
String ret;
int wifiState = wifiMan.getWifiState();
wifiMan.setWifiEnabled(true);
File fl = new File(fileAddressMac);
FileInputStream fin = new FileInputStream(fl);
StringBuilder builder = new StringBuilder();
int ch;
while((ch = fin.read()) != -1){
builder.append((char)ch);
}
ret = builder.toString();
fin.close();
boolean enabled = WifiManager.WIFI_STATE_ENABLED == wifiState;
wifiMan.setWifiEnabled(enabled);
return ret;
}
manifest :
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
Summary : this code will try to get the MAC address first by Interface and if its failed it get it by file system.
Note:for file System way, you need to enable WIFI to access the file.
thnx to Sam's answer here https://stackoverflow.com/a/39288868/3818437
You can get the MAC address from the IPv6 local address. E.g., the IPv6 address "fe80::1034:56ff:fe78:9abc" corresponds to the MAC address "12-34-56-78-9a-bc". See the code below. Getting the WiFi IPv6 address only requires android.permission.INTERNET.
See the Wikipedia page IPv6 address, particularly the note about "local addresses" fe80::/64 and the section about "Modified EUI-64".
/**
* Gets an EUI-48 MAC address from an IPv6 link-local address.
* E.g., the IPv6 address "fe80::1034:56ff:fe78:9abc"
* corresponds to the MAC address "12-34-56-78-9a-bc".
* <p/>
* See the note about "local addresses" fe80::/64 and the section about "Modified EUI-64" in
* the Wikipedia article "IPv6 address" at https://en.wikipedia.org/wiki/IPv6_address
*
* #param ipv6 An Inet6Address object.
* #return The EUI-48 MAC address as a byte array, null on error.
*/
private static byte[] getMacAddressFromIpv6(final Inet6Address ipv6)
{
byte[] eui48mac = null;
if (ipv6 != null) {
/*
* Make sure that this is an fe80::/64 link-local address.
*/
final byte[] ipv6Bytes = ipv6.getAddress();
if ((ipv6Bytes != null) &&
(ipv6Bytes.length == 16) &&
(ipv6Bytes[0] == (byte) 0xfe) &&
(ipv6Bytes[1] == (byte) 0x80) &&
(ipv6Bytes[11] == (byte) 0xff) &&
(ipv6Bytes[12] == (byte) 0xfe)) {
/*
* Allocate a byte array for storing the EUI-48 MAC address, then fill it
* from the appropriate bytes of the IPv6 address. Invert the 7th bit
* of the first byte and discard the "ff:fe" portion of the modified
* EUI-64 MAC address.
*/
eui48mac = new byte[6];
eui48mac[0] = (byte) (ipv6Bytes[8] ^ 0x2);
eui48mac[1] = ipv6Bytes[9];
eui48mac[2] = ipv6Bytes[10];
eui48mac[3] = ipv6Bytes[13];
eui48mac[4] = ipv6Bytes[14];
eui48mac[5] = ipv6Bytes[15];
}
}
return eui48mac;
}
I try to get mac address with 2 methods, first by Interface and if its failed, i get it by file system, but you need to enable wifi to access the file.
//Android 6.0 : Access to mac address from WifiManager forbidden
private static final String marshmallowMacAddress = "02:00:00:00:00:00";
private static final String fileAddressMac = "/sys/class/net/wlan0/address";
public static String recupAdresseMAC(WifiManager wifiMan) {
WifiInfo wifiInf = wifiMan.getConnectionInfo();
if(wifiInf.getMacAddress().equals(marshmallowMacAddress)){
String ret = null;
try {
ret= getAdressMacByInterface();
if (ret != null){
return ret;
} else {
ret = getAddressMacByFile(wifiMan);
return ret;
}
} catch (IOException e) {
Log.e("MobileAccess", "Erreur lecture propriete Adresse MAC");
} catch (Exception e) {
Log.e("MobileAcces", "Erreur lecture propriete Adresse MAC ");
}
} else{
return wifiInf.getMacAddress();
}
return marshmallowMacAddress;
}
private static String getAdressMacByInterface(){
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (nif.getName().equalsIgnoreCase("wlan0")) {
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return "";
}
StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
res1.append(String.format("%02X:",b));
}
if (res1.length() > 0) {
res1.deleteCharAt(res1.length() - 1);
}
return res1.toString();
}
}
} catch (Exception e) {
Log.e("MobileAcces", "Erreur lecture propriete Adresse MAC ");
}
return null;
}
private static String getAddressMacByFile(WifiManager wifiMan) throws Exception {
String ret;
int wifiState = wifiMan.getWifiState();
wifiMan.setWifiEnabled(true);
File fl = new File(fileAddressMac);
FileInputStream fin = new FileInputStream(fl);
ret = convertStreamToString(fin);
fin.close();
boolean enabled = WifiManager.WIFI_STATE_ENABLED == wifiState;
wifiMan.setWifiEnabled(enabled);
return ret;
}
Add this line to your manifest.
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
I recommend you to persist your mac address in your preferences like here
mac = activity.getSharedPreferences("MAC_ADDRESS", Context.MODE_PRIVATE).getString("MAC_ADDRESS", "");
if(mac == null || mac.equals("")){
WifiManager wifiMan = (WifiManager) activity.getSystemService(Context.WIFI_SERVICE);
mac = MobileAccess.recupAdresseMAC(wifiMan);
if(mac != null && !mac.equals("")){
SharedPreferences.Editor editor = activity.getSharedPreferences("MAC_ADDRESS", Context.MODE_PRIVATE).edit();
editor.putString("MAC_ADDRESS", mac).commit();
}
}
Its Perfectly Fine
package com.keshav.fetchmacaddress;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.List;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e("keshav","getMacAddr -> " +getMacAddr());
}
public static String getMacAddr() {
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (!nif.getName().equalsIgnoreCase("wlan0")) continue;
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return "";
}
StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
res1.append(Integer.toHexString(b & 0xFF) + ":");
}
if (res1.length() > 0) {
res1.deleteCharAt(res1.length() - 1);
}
return res1.toString();
}
} catch (Exception ex) {
//handle exception
}
return "";
}
}
First you need to add Internet user permission.
<uses-permission android:name="android.permission.INTERNET" />
Then you can find the mac over the NetworkInterfaces API.
public static String getMacAddr() {
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (!nif.getName().equalsIgnoreCase("wlan0")) continue;
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return "";
}
StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
res1.append(String.format("%02X:",b));
}
if (res1.length() > 0) {
res1.deleteCharAt(res1.length() - 1);
}
return res1.toString();
}
} catch (Exception ex) {
}
return "02:00:00:00:00:00";
}
The answers are mostly correct, but keep care, that there is a change in android 7. You will need to use the
DevicePolicyManager and the Method getWifiMacAddress. The official docs has a typo, which means that you shouldnt copy/paste it from there.
DevicePolicyManager.getWifiMacAddress()
Refs:
https://developer.android.com/about/versions/nougat/android-7.0-changes.html
Get Device mac adress in Android Nougat and O programmatically
Use wifiInfo.getBSSID() to get Mac Address of AccessPoint instead of getMacAddress method.
This is a more kotlin way to get Mac Address
fun getMacAddress(): String =
NetworkInterface.getNetworkInterfaces().toList()
.firstOrNull { it.name.equals("wlan0", ignoreCase = true) }?.let {
it.hardwareAddress?.let { macBytes ->
StringBuilder().apply {
for (b in macBytes) {
append(String.format("%02X:", b))
}
if (isNotEmpty()) {
deleteCharAt(lastIndex)
}
}
}.toString()
} ?: "02:00:00:00:00:00"
I was been trying to find whether a device is rooted or not and if the device is found rooted i do not want my application to get installed.I have tried two of the below methods
private boolean isRooted() {
return findBinary("su");
}
public static boolean findBinary(String binaryName) {
boolean found = false;
if (!found) {
String[] places = { "/sbin/", "/system/bin/", "/system/xbin/",
"/data/local/xbin/", "/data/local/bin/",
"/system/sd/xbin/", "/system/bin/failsafe/", "/data/local/" };
for (String where : places) {
if (new File(where + binaryName).exists()) {
found = true;
break;
}
}
try {
File file = new File("/system/app/Superuser.apk");
if (file.exists()) {
Log.e("ERROR", "Unable to find icon for package '"
+ "apk found");
found = true;
}
} catch (Exception e1) {
// ignore
}
}
return found;
}
But i don't think that these methods are enough to find a rooted device,since there are tools to hide an apk and the su file can be renamed or deleted.Is there any other way or any suggestions which is 100 percent reliable to find a rooted device?
I was trying to edit the su but couldn't do anything.Is it just a word of mouth or really possible to do so? Thanks in advance
***EDITED***:
I have used "HIDE MY ROOT" application to hide the SU binary aswell as superuser.apk.I can make my rooted device, look like unrooted using hide my root application.Therefore i can say that this source is falseproof and not completely reliable to find rooted device.
Kindly let me know if there is any alternative way to find the rooted device..
I did this in the following way :
/*
* Run su command on device
* #throws IOException, InterruptedException
*/
private static boolean suRun() throws IOException, InterruptedException
{
try {
Process su = null;
su = Runtime.getRuntime().exec(new String[] {"su","-c","exit"});
su.waitFor();
InputStream in = su.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
String suOutput = bufferedReader.readLine();
if (suOutput == null)
{
return true;
}
else
{
return false;
}
} catch (Exception ex)
{
return false;
}
}
public static boolean isPhoneRooted() {
// check if /system/app/Superuser.apk is present and can run su
try {
File file = new File("/system/app/Superuser.apk");
if (file.exists() && suRun()) {
Log.d("Blocking Service", "ROOTED PHONE DETECTED");
return true;
}
}
catch (Throwable e1) {
// ignore
}
return false;
}
you can use SafetyNet API from google play service. this is what being used by android pay not only for the root detection but also to check compatibility with android CTS.
Call isRooted() from ShellInterface
isRooted() depend upon majority of three factor
public static boolean isRooted() {
return isRooted1() ? (isRooted2() || isRooted3()) : (isRooted2() && isRooted3());
}
private static boolean isRooted1() {
Process mProcess = null;
boolean mRoot;
try {
// This is executing on terminal
mProcess = Runtime.getRuntime().exec("su");
mRoot = true;
// If the execute successfully then it return to true
} catch (Exception e) {
// if is not successfully then it return to false
mRoot = false;
} finally {
if (mProcess != null) {
try {
mProcess.destroy();
} catch (Exception ignored) {
}
}
}
return mRoot;
}
private static boolean isRooted2() {
String buildTags = Build.TAGS;
return buildTags !=null && buildTags.contains("test-keys");
}
private static boolean isRooted3() {
boolean mRoot = false;
boolean found = false;
if (!found) {
String[] places = {"/sbin/", "/system/bin/","/system/xbin",
"/data/local/xbin","/system/sd/xbin","/data/local"
};
for (String path : places){
if (new File(path+"su").exists()) {
mRoot = true;
found = true;
}
}
}
return mRoot;
}
I want to get the state of the system and then show a dialog (toasts at the moment so the code is short) and allow the user to either mount it rw or ro based on the current state.
I used the following code but it didn't work and I'm confused as to why it's not working.
File system = new File("/system");
if(system.canWrite()){
Toast.makeText(Utilities.this, "System is RW", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(Utilities.this, "System is RO", Toast.LENGTH_SHORT).show();
}
How can this be done?
===============================EDIT===============================
Here is the final code after parsing /proc/mounts for future searchers
private boolean readReadWriteFile() {
File mountFile = new File("/proc/mounts");
StringBuilder procData = new StringBuilder();
if(mountFile.exists()) {
try {
FileInputStream fis = new FileInputStream(mountFile.toString());
DataInputStream dis = new DataInputStream(fis);
BufferedReader br = new BufferedReader(new InputStreamReader(dis));
String data;
while((data = br.readLine()) != null) {
procData.append(data + "\n");
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
if(procData.toString() != null) {
String[] tmp = procData.toString().split("\n");
for(int x = 0; x < tmp.length; x++) {
//Kept simple here on purpose different devices have different blocks
if(tmp[x].contains("/dev/block") && tmp[x].contains("/system")) {
if(tmp[x].contains("rw")) {
Toast.makeText(Activity.this, "System is rw", Toast.LENGTH_LONG).show();
return true;
} else if(tmp[x].contains("ro")) {
Toast.makeText(Activity.this, "System is ro", Toast.LENGTH_LONG).show();
return false;
} else {
return false;
}
}
}
}
}
return false;
}
That's because you query permissions for user. Even if /system is remounted as rw it does not mean your application will get "write" access to it.
As alternative solution, read /proc/mounts file and parse ro/rw status from there.
for some specific requirement
I am required to change Android Default Home application
with my customized Home application ( a setting inside my app that will toggle default home = my application or previous home)
I don't want the user to travel android settings that are very complicated.
Can any one help me out like where it registers launcher.apk for default
home application or how to change that
The only thing I could find was that old question: How to change default Android's Desktop application?
but no answers at all.
I have seen HomeSwitcher in the Market that do the trick, but no answer for the developer that might certainly be busy.
EDIT
I found this on the web http://www.mail-archive.com/android-developers#googlegroups.com/msg74167.html
But I got the same issue:
this is my code:
private void makePreferred() {
PackageManager pm = getPackageManager();
IntentFilter f = new IntentFilter("android.intent.action.MAIN");
f.addCategory("android.intent.category.HOME");
f.addCategory("android.intent.category.DEFAULT");
ComponentName cn = new ComponentName("com.example.android.home", "com.example.android.home.Home");
pm.addPreferredActivity(f, IntentFilter.MATCH_CATEGORY_EMPTY, null, cn);
I have the android.permission.SET_PREFERRED_APPLICATIONS set in the
manifest. After executing the code above, the logs claim things have
been added like expected (same logs as when I tick off "Make default"
from IntentResolver's list). However, when I proceed by clicking home,
the list still shows up and the logs say:
INFO/PackageManager(52): Result set changed, dropping preferred
activity for Intent { act=android.intent.action.MAIN cat=
[android.intent.category.HOME] flg=0x10200000 } type null
So it seems the resolver deletes the default entry. Am I doing
something wrong, or is this a security measure? What are the ideas
behind this?
I did an extensive research on that and starting from 2.2 there is no way to do that. The only way is using some hacking that toddler lock app does but this app put samsung phones recently in the infinite loop, so it is a risky approach.
if you look at the froyo source code here of packagemanager class, you will see this small condition in the addPreferredActivity method:
if (getUidTargetSdkVersionLockedLP(Binder.getCallingUid())
< Build.VERSION_CODES.FROYO) {
Slog.w(TAG, "Ignoring addPreferredActivity() from uid"
+ Binder.getCallingUid());
return;
}
HomeSwitcher does not work properly on 2.2 since it uses this very method and developer made a comment on app page "Froyo(2.2) is not supported
due to the API change"
"Result set changed" means that the set of packages matching that intent has changed from the set you specified when you created the default - - so the default is no longer valid. Your list of components (which you are currently setting to null) needs to contain all homescreen apps present on device, not just yours.
Here's example code that I have tested (using adb shell am start http://www.google.co.uk/ ) and used to set the default browser. XXX represents a customer name that I had to black out.
Note that in order to call addPreferredActivity you must have compiled against a minimum-sdk version of 8 (2.2) and you must have specified the SET_PREFERRED_APPLICATIONS permission. That permission is protection level 2, so you need to be signed with the same certificate as PackageManager.
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.VIEW");
filter.addCategory("android.intent.category.DEFAULT");
filter.addDataScheme("http");
Context context = getApplicationContext();
ComponentName component = new ComponentName("com.opera.mini.XXX", "com.opera.mini.XXX.CustomerBrowser");
ComponentName[] components = new ComponentName[] {new ComponentName("com.android.browser", "com.android.browser.BrowserActivity"),
component};
pm.addPreferredActivity(filter, IntentFilter.MATCH_CATEGORY_SCHEME, components, component);
ETA - if you marked this response down, could you let me know why. The code I posted above is tested and working...
startActivity(new Intent(Settings.ACTION_HOME_SETTINGS));
This code works on my ICS device: I use a service that is sensible to some call, one of them is called SET_PREFERRED_LAUNCHER, the put in a bundle your new default Launcher package (PREFERRED_PACKAGE_KEY) and it's activity (PREFERRED_ACTIVITY_KEY)
Method installPackageMethod = null;
Method deletePackageMethod = null;
Method setPreferredActivityMethod = null;
Method replacePreferredActivityMethod = null;
Object pm = null;
#Override
public void onCreate() {
super.onCreate();
if (pm == null)
pm = getPackageManager();
try {
if (setPreferredActivityMethod == null)
setPreferredActivityMethod = pm.getClass().getMethod(
"addPreferredActivity", IntentFilter.class, int.class,
ComponentName[].class, ComponentName.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
private final class ServiceHandler extends Handler {
private Context context;
public ServiceHandler(Looper looper, Context ctx) {
super(looper);
context = ctx;
}
#Override
public void handleMessage(Message msg) {
Intent intent = (Intent) msg.getData().getParcelable(
UPDATER_SERVICE_ACTION);
int request = intent.getIntExtra(
REQUEST_KEY,
REQUEST_UNKNOWN);
Bundle bundle = intent.getExtras();
switch (request) {
case INSTALL_APPLICATION: {
if (bundle != null) {
String appPath = bundle
.getString(APP_PATH_KEY);
if (appPath != null) {
LogUtil.e(TAG, "try to install " + appPath);
try {
am.installPackage(appPath);
} catch (Exception e) {
e.printStackTrace();
}
LogUtil.e(TAG, "install of " + appPath + " done");
}
}
break;
}
case UNISTALL_PACKAGE: {
if (bundle != null) {
String packagename = bundle
.getString(PACKAGE_NAME_KEY);
if (packagename != null) {
LogUtil.e(TAG, "unistall " + packagename);
try {
deletePackageMethod
.invoke(pm, packagename, null, 0);
} catch (Exception e) {
e.printStackTrace();
}
}
}
break;
}
case SET_PREFERRED_LAUNCHER: {
if (bundle != null) {
String package_name = bundle
.getString(PREFERRED_PACKAGE_KEY);
if (package_name == null) {
LogUtil.e(TAG,
"WARNING: setDefaultActivity cannot continue, package is NULL");
return;
}
String activity_name = bundle
.getString(PREFERRED_ACTIVITY_KEY);
if (activity_name == null) {
LogUtil.e(TAG,
"WARNING: setDefaultActivity cannot continue, activity is NULL");
return;
}
LogUtil.e(TAG, "setDefaultActivity activity="
+ activity_name + " package=" + package_name);
IntentFilter filter = new IntentFilter(
"android.intent.action.MAIN");
filter.addCategory("android.intent.category.HOME");
filter.addCategory("android.intent.category.DEFAULT");
ComponentName[] components = new ComponentName[] {
new ComponentName("com.android.launcher",
"com.android.launcher2.Launcher"),
new ComponentName(package_name, activity_name) };
ComponentName activity = new ComponentName(package_name,
activity_name);
try {
setPreferredActivityMethod.invoke(pm, filter,
IntentFilter.MATCH_CATEGORY_EMPTY, components,
activity);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
Remember to add in your manifest file this permission:
<uses-permission android:name="android.permission.SET_PREFERRED_APPLICATIONS"/>
Usage:
public void setPreferredLauncher(String activity_name,String package_name)
{
Intent intent = new Intent(UPDATER_SERVICE_ACTION);
intent.putExtra(REQUEST_KEY, SET_PREFERRED_LAUNCHER);
intent.putExtra(PREFERRED_ACTIVITY_KEY, activity_name);
intent.putExtra(PREFERRED_PACKAGE_KEY, package_name);
context.startService(intent);
}
where:
public static final String _UPDATER_SERVICE_ACTION = "com.android.updaterservice.ACTION";
public static final String REQUEST_KEY = "com.android.updaterservice.REQUEST_KEY";
public static final String PACKAGE_NAME_KEY = "com.android.updaterservice.PACKAGE_NAME_KEY";
public static final String APP_PATH_KEY = "com.android.updaterservice.APP_PATH_KEY";
public static final String PREFERRED_ACTIVITY_KEY = "com.android.updaterservice.PREFERRED_ACTIVITY_KEY";
public static final String PREFERRED_PACKAGE_KEY = "com.android.updaterservice.PREFERRED_PACKAGE_KEY";
public static final String INSTALL_PACKAGE_RESULT = "com.android.updaterservice.INSTALL_PACKAGE_RESULT";
public static final String PACKAGE_NAME = "PACKAGE_NAME";
public static final String INSTALL_SUCCEEDED = "INSTALL_SUCCEEDED";
public static final int REQUEST_UNKNOWN = -1;
public static final int INSTALL_APPLICATION = 1;
public static final int UNISTALL_PACKAGE = 2;
public static final int SET_PREFERRED_LAUNCHER = 3;