Incorporate home metadata into app details UI
Just as we don't expose disable/uninstall of home applications in this UI,
we now also don't allow disable/uninstall of packages that are proxying
for home apps via the android.app.home.alternate meta-data mechanism.
Also, don't display the 'Home' settings top-level category at all when
there is only a single available home app.
Finally, explicitly note the current user when sending broadcasts,
otherwise API sanity checks get suspicious.
Bug 10749961
Change-Id: I13e11032cb571df19798c4e13c91a572d1ee61a7
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 6b6e704..5c77a97 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -564,7 +564,9 @@
int headerIndex = i + 1;
i = insertAccountsHeaders(target, headerIndex);
} else if (id == R.id.home_settings) {
- updateHomeSettingHeaders(header);
+ if (!updateHomeSettingHeaders(header)) {
+ target.remove(i);
+ }
} else if (id == R.id.user_settings) {
if (!UserHandle.MU_ENABLED
|| !UserManager.supportsMultipleUsers()
@@ -669,11 +671,16 @@
return ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
}
- private void updateHomeSettingHeaders(Header header) {
+ private boolean updateHomeSettingHeaders(Header header) {
final PackageManager pm = getPackageManager();
final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
try {
ComponentName currentHome = pm.getHomeActivities(homeApps);
+ if (homeApps.size() < 2) {
+ // When there's only one available home app, omit this settings
+ // category entirely at the top level UI.
+ return false;
+ }
ResolveInfo iconSource = null;
if (currentHome == null) {
// no current default, so find the system home app and use that
@@ -708,6 +715,7 @@
// Can't look up the home activity; bail on configuring the icon
Log.w(LOG_TAG, "Problem looking up home activity!", e);
}
+ return true;
}
private void getMetaData() {
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index 37c33db..1b3938c 100644
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -66,6 +66,7 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -143,6 +144,8 @@
private PackageMoveObserver mPackageMoveObserver;
+ private final HashSet<String> mHomePackages = new HashSet<String>();
+
private boolean mDisableAfterUninstall;
private boolean mHaveSizes = false;
@@ -320,29 +323,20 @@
private boolean handleDisableable(Button button) {
boolean disableable = false;
- 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) || isThisASystemPackage()) {
- // Disable button for core system applications.
- button.setText(R.string.disable_text);
- } else if (mAppEntry.info.enabled) {
- button.setText(R.string.disable_text);
- disableable = true;
- } else {
- button.setText(R.string.enable_text);
- disableable = true;
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Unable to get package info", e);
+ // 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.
+ if (mHomePackages.contains(mAppEntry.info.packageName) || isThisASystemPackage()) {
+ // Disable button for core system applications.
+ button.setText(R.string.disable_text);
+ } else if (mAppEntry.info.enabled) {
+ button.setText(R.string.disable_text);
+ disableable = true;
+ } else {
+ button.setText(R.string.enable_text);
+ disableable = true;
}
+
return disableable;
}
@@ -638,6 +632,21 @@
return packageName;
}
+ private boolean signaturesMatch(String pkg1, String pkg2) {
+ if (pkg1 != null && pkg2 != null) {
+ try {
+ final int match = mPm.checkSignatures(pkg1, pkg2);
+ if (match >= PackageManager.SIGNATURE_MATCH) {
+ return true;
+ }
+ } catch (Exception e) {
+ // e.g. named alternate package not found during lookup;
+ // this is an expected case sometimes
+ }
+ }
+ return false;
+ }
+
private boolean refreshUi() {
if (mMoveInProgress) {
return true;
@@ -652,6 +661,25 @@
return false; // onCreate must have failed, make sure to exit
}
+ // Get list of "home" apps and trace through any meta-data references
+ List<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
+ mPm.getHomeActivities(homeActivities);
+ mHomePackages.clear();
+ for (int i = 0; i< homeActivities.size(); i++) {
+ ResolveInfo ri = homeActivities.get(i);
+ final String activityPkg = ri.activityInfo.packageName;
+ mHomePackages.add(activityPkg);
+
+ // Also make sure to include anything proxying for the home app
+ final Bundle metadata = ri.activityInfo.metaData;
+ if (metadata != null) {
+ final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE);
+ if (signaturesMatch(metaPkg, activityPkg)) {
+ mHomePackages.add(metaPkg);
+ }
+ }
+ }
+
// Get list of preferred activities
List<ComponentName> prefActList = new ArrayList<ComponentName>();
@@ -1237,8 +1265,8 @@
intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName });
intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid);
intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid));
- getActivity().sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null,
- Activity.RESULT_CANCELED, null, null);
+ getActivity().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
+ mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null);
}
}