Use a framework resource to name a factory-default browser app
If there is no resource-named default but there is a single factory-
installed browser app, that app is made the titular default.
This also introduces a permission guard on attempts to set the
default browser or the app-link state. These operations are now
contingent on the existing SET_PREFERRED_APPLICATIONS permission.
Bug 21778406
Change-Id: Id099bb9c4141f28917546492657cd2fba472e6b6
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f6cefba..9d79952 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1268,6 +1268,14 @@
application is desired. -->
<string name="default_sms_application" translatable="false">com.android.mms</string>
+ <!-- Default web browser. This is the package name of the application that will
+ be the default browser when the device first boots. Afterwards the user
+ can select whatever browser app they wish to use as the default.
+
+ If this string is empty or the specified package does not exist, then
+ the behavior will be as though no app was named as an explicit default. -->
+ <string name="default_browser" translatable="false"></string>
+
<!-- Enable/disable default bluetooth profiles:
HSP_AG, ObexObjectPush, Audio, NAP -->
<bool name="config_bluetooth_default_profiles">true</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5c42d04..8bf1aac 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -903,6 +903,7 @@
<java-symbol type="string" name="sipAddressTypeOther" />
<java-symbol type="string" name="sipAddressTypeWork" />
<java-symbol type="string" name="default_sms_application" />
+ <java-symbol type="string" name="default_browser" />
<java-symbol type="string" name="sms_control_message" />
<java-symbol type="string" name="sms_control_title" />
<java-symbol type="string" name="sms_control_no" />
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 40ff3f4..d771085 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2191,7 +2191,8 @@
// If this is the first boot, and it is a normal boot, then
// we need to initialize the default preferred apps.
if (!mRestoredSettings && !onlyCore) {
- mSettings.readDefaultPreferredAppsLPw(this, 0);
+ mSettings.applyDefaultPreferredAppsLPw(this, UserHandle.USER_OWNER);
+ applyFactoryDefaultBrowserLPw(UserHandle.USER_OWNER);
}
// If this is first boot after an OTA, and a normal boot, then
@@ -2375,13 +2376,72 @@
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "End priming domain verifications");
}
+ private void applyFactoryDefaultBrowserLPw(int userId) {
+ // The default browser app's package name is stored in a string resource,
+ // with a product-specific overlay used for vendor customization.
+ String browserPkg = mContext.getResources().getString(
+ com.android.internal.R.string.default_browser);
+ if (browserPkg != null) {
+ // non-empty string => required to be a known package
+ PackageSetting ps = mSettings.mPackages.get(browserPkg);
+ if (ps == null) {
+ Slog.e(TAG, "Product default browser app does not exist: " + browserPkg);
+ browserPkg = null;
+ } else {
+ mSettings.setDefaultBrowserPackageNameLPw(browserPkg, userId);
+ }
+ }
+
+ // Nothing valid explicitly set? Make the factory-installed browser the explicit
+ // default. If there's more than one, just leave everything alone.
+ if (browserPkg == null) {
+ calculateDefaultBrowserLPw(userId);
+ }
+ }
+
+ private void calculateDefaultBrowserLPw(int userId) {
+ List<String> allBrowsers = resolveAllBrowserApps(userId);
+ final String browserPkg = (allBrowsers.size() == 1) ? allBrowsers.get(0) : null;
+ mSettings.setDefaultBrowserPackageNameLPw(browserPkg, userId);
+ }
+
+ private List<String> resolveAllBrowserApps(int userId) {
+ // Match all generic http: browser apps
+ Intent intent = new Intent();
+ intent.setAction(Intent.ACTION_VIEW);
+ intent.addCategory(Intent.CATEGORY_BROWSABLE);
+ intent.setData(Uri.parse("http:"));
+
+ // Resolve that intent and check that the handleAllWebDataURI boolean is set
+ List<ResolveInfo> list = queryIntentActivities(intent, null, 0, userId);
+
+ final int count = list.size();
+ List<String> result = new ArrayList<String>(count);
+ for (int i=0; i<count; i++) {
+ ResolveInfo info = list.get(i);
+ if (info.activityInfo == null
+ || !info.handleAllWebDataURI
+ || (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
+ || result.contains(info.activityInfo.packageName)) {
+ continue;
+ }
+ result.add(info.activityInfo.packageName);
+ }
+
+ return result;
+ }
+
private void checkDefaultBrowser() {
final int myUserId = UserHandle.myUserId();
final String packageName = getDefaultBrowserPackageName(myUserId);
- PackageInfo info = getPackageInfo(packageName, 0, myUserId);
- if (info == null) {
- Slog.w(TAG, "Default browser no longer installed: " + packageName);
- setDefaultBrowserPackageName(null, myUserId);
+ if (packageName != null) {
+ PackageInfo info = getPackageInfo(packageName, 0, myUserId);
+ if (info == null) {
+ Slog.w(TAG, "Default browser no longer installed: " + packageName);
+ synchronized (mPackages) {
+ applyFactoryDefaultBrowserLPw(myUserId); // leaves ambiguous when > 1
+ }
+ }
}
}
@@ -4443,7 +4503,7 @@
synchronized (mPackages) {
final int count = candidates.size();
- // First, try to use the domain prefered App. Partition the candidates into four lists:
+ // First, try to use the domain preferred app. Partition the candidates into four lists:
// one for the final results, one for the "do not use ever", one for "undefined status"
// and finally one for "Browser App type".
for (int n=0; n<count; n++) {
@@ -9577,6 +9637,9 @@
@Override
public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
+
boolean result = false;
synchronized (mPackages) {
result = mSettings.updateIntentFilterVerificationStatusLPw(packageName, status, userId);
@@ -9618,8 +9681,11 @@
@Override
public boolean setDefaultBrowserPackageName(String packageName, int userId) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
+
synchronized (mPackages) {
- boolean result = mSettings.setDefaultBrowserPackageNameLPr(packageName, userId);
+ boolean result = mSettings.setDefaultBrowserPackageNameLPw(packageName, userId);
if (packageName != null) {
result |= updateIntentVerificationStatus(packageName,
PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
@@ -13379,15 +13445,15 @@
@Override
public void resetPreferredActivities(int userId) {
- /* TODO: Actually use userId. Why is it being passed in? */
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
// writer
synchronized (mPackages) {
- int user = UserHandle.getCallingUserId();
- clearPackagePreferredActivitiesLPw(null, user);
- mSettings.readDefaultPreferredAppsLPw(this, user);
- scheduleWritePackageRestrictionsLocked(user);
+ clearPackagePreferredActivitiesLPw(null, userId);
+ mSettings.applyDefaultPreferredAppsLPw(this, userId);
+ applyFactoryDefaultBrowserLPw(userId);
+
+ scheduleWritePackageRestrictionsLocked(userId);
}
}
@@ -15449,6 +15515,7 @@
if (mInstaller != null) {
mInstaller.createUserConfig(userHandle);
mSettings.createNewUserLILPw(this, mInstaller, userHandle, path);
+ applyFactoryDefaultBrowserLPw(userHandle);
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index dcd7799..58d703a 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1130,7 +1130,7 @@
return result;
}
- boolean setDefaultBrowserPackageNameLPr(String packageName, int userId) {
+ boolean setDefaultBrowserPackageNameLPw(String packageName, int userId) {
if (userId == UserHandle.USER_ALL) {
return false;
}
@@ -2710,7 +2710,7 @@
return true;
}
- void readDefaultPreferredAppsLPw(PackageManagerService service, int userId) {
+ void applyDefaultPreferredAppsLPw(PackageManagerService service, int userId) {
// First pull data from any pre-installed apps.
for (PackageSetting ps : mPackages.values()) {
if ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0 && ps.pkg != null
@@ -3618,7 +3618,7 @@
UserHandle.getUid(userHandle, ps.appId), userHandle,
ps.pkg.applicationInfo.seinfo);
}
- readDefaultPreferredAppsLPw(service, userHandle);
+ applyDefaultPreferredAppsLPw(service, userHandle);
writePackageRestrictionsLPr(userHandle);
writePackageListLPr(userHandle);
}