Add a mechanism for products to specify default active app linkages

We now have a new tag type in the existing SystemConfig XML files:

    <app-link package="..." />

Packages mentioned here are treated as though the user had instructed
the system to always open the app rather than a browser when touching
an HTTP/HTTPs URL that is handled by the app.  This default state can
of course still be turned down by the user if they choose.

Bug 21595872

Change-Id: I2824064a891bba3867ee86f81d609d22e83aef7a
This commit is contained in:
Christopher Tate
2015-07-07 18:10:38 -07:00
parent 84005b4049
commit 01e186437f
2 changed files with 109 additions and 63 deletions

View File

@@ -90,6 +90,10 @@ public class SystemConfig {
// These are the app package names that should not allow IME switching.
final ArraySet<String> mFixedImeApps = new ArraySet<>();
// These are the package names of apps which should be in the 'always'
// URL-handling state upon factory reset.
final ArraySet<String> mLinkedApps = new ArraySet<>();
public static SystemConfig getInstance() {
synchronized (SystemConfig.class) {
if (sInstance == null) {
@@ -127,6 +131,10 @@ public class SystemConfig {
return mFixedImeApps;
}
public ArraySet<String> getLinkedApps() {
return mLinkedApps;
}
SystemConfig() {
// Read configuration from system
readPermissions(Environment.buildPath(
@@ -343,6 +351,16 @@ public class SystemConfig {
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("app-link".equals(name)) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<app-link> without package in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mLinkedApps.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
} else {
XmlUtils.skipCurrentTag(parser);
continue;

View File

@@ -709,7 +709,8 @@ public class PackageManagerService extends IPackageManager.Stub {
return;
}
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"Updating IntentFilterVerificationInfo for verificationId:" + verificationId);
"Updating IntentFilterVerificationInfo for package " + packageName
+" verificationId:" + verificationId);
synchronized (mPackages) {
if (verified) {
@@ -794,8 +795,6 @@ public class PackageManagerService extends IPackageManager.Stub {
boolean hasHTTPorHTTPS = filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
filter.hasDataScheme(IntentFilter.SCHEME_HTTPS);
if (!hasHTTPorHTTPS) {
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"IntentFilter does not contain any HTTP or HTTPS data scheme");
return false;
}
return true;
@@ -2245,6 +2244,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (!mRestoredSettings && !onlyCore) {
mSettings.applyDefaultPreferredAppsLPw(this, UserHandle.USER_OWNER);
applyFactoryDefaultBrowserLPw(UserHandle.USER_OWNER);
primeDomainVerificationsLPw(UserHandle.USER_OWNER);
}
// If this is first boot after an OTA, and a normal boot, then
@@ -2259,7 +2259,6 @@ public class PackageManagerService extends IPackageManager.Stub {
mSettings.mFingerprint = Build.FINGERPRINT;
}
primeDomainVerificationsLPw();
checkDefaultBrowser();
// All the changes are done during package scanning.
@@ -2412,54 +2411,56 @@ public class PackageManagerService extends IPackageManager.Stub {
return verifierComponentName;
}
private void primeDomainVerificationsLPw() {
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Start priming domain verifications");
boolean updated = false;
ArraySet<String> allHostsSet = new ArraySet<>();
for (PackageParser.Package pkg : mPackages.values()) {
final String packageName = pkg.packageName;
if (!hasDomainURLs(pkg)) {
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "No priming domain verifications for " +
"package with no domain URLs: " + packageName);
continue;
}
if (!pkg.isSystemApp()) {
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"No priming domain verifications for a non system package : " +
packageName);
continue;
}
for (PackageParser.Activity a : pkg.activities) {
for (ActivityIntentInfo filter : a.intents) {
if (hasValidDomains(filter)) {
allHostsSet.addAll(filter.getHostsList());
private void primeDomainVerificationsLPw(int userId) {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.d(TAG, "Priming domain verifications in user " + userId);
}
SystemConfig systemConfig = SystemConfig.getInstance();
ArraySet<String> packages = systemConfig.getLinkedApps();
ArraySet<String> domains = new ArraySet<String>();
for (String packageName : packages) {
PackageParser.Package pkg = mPackages.get(packageName);
if (pkg != null) {
if (!pkg.isSystemApp()) {
Slog.w(TAG, "Non-system app '" + packageName + "' in sysconfig <app-link>");
continue;
}
domains.clear();
for (PackageParser.Activity a : pkg.activities) {
for (ActivityIntentInfo filter : a.intents) {
if (hasValidDomains(filter)) {
domains.addAll(filter.getHostsList());
}
}
}
if (domains.size() > 0) {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.v(TAG, " + " + packageName);
}
// 'Undefined' in the global IntentFilterVerificationInfo, i.e. the usual
// state w.r.t. the formal app-linkage "no verification attempted" state;
// and then 'always' in the per-user state actually used for intent resolution.
final IntentFilterVerificationInfo ivi;
ivi = mSettings.createIntentFilterVerificationIfNeededLPw(packageName,
new ArrayList<String>(domains));
ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
mSettings.updateIntentFilterVerificationStatusLPw(packageName,
INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, userId);
} else {
Slog.w(TAG, "Sysconfig <app-link> package '" + packageName
+ "' does not handle web links");
}
} else {
Slog.w(TAG, "Unknown package '" + packageName + "' in sysconfig <app-link>");
}
if (allHostsSet.size() == 0) {
allHostsSet.add("*");
}
ArrayList<String> allHostsList = new ArrayList<>(allHostsSet);
IntentFilterVerificationInfo ivi =
mSettings.createIntentFilterVerificationIfNeededLPw(packageName, allHostsList);
if (ivi != null) {
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"Priming domain verifications for package: " + packageName +
" with hosts:" + ivi.getDomainsString());
ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS);
updated = true;
}
else {
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"No priming domain verifications for package: " + packageName);
}
allHostsSet.clear();
}
if (updated) {
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"Will need to write primed domain verifications");
}
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "End priming domain verifications");
scheduleWritePackageRestrictionsLocked(userId);
scheduleWriteSettingsLocked();
}
private void applyFactoryDefaultBrowserLPw(int userId) {
@@ -2467,7 +2468,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// with a product-specific overlay used for vendor customization.
String browserPkg = mContext.getResources().getString(
com.android.internal.R.string.default_browser);
if (browserPkg != null) {
if (!TextUtils.isEmpty(browserPkg)) {
// non-empty string => required to be a known package
PackageSetting ps = mSettings.mPackages.get(browserPkg);
if (ps == null) {
@@ -13691,6 +13692,7 @@ public class PackageManagerService extends IPackageManager.Stub {
clearPackagePreferredActivitiesLPw(null, userId);
mSettings.applyDefaultPreferredAppsLPw(this, userId);
applyFactoryDefaultBrowserLPw(userId);
primeDomainVerificationsLPw(userId);
scheduleWritePackageRestrictionsLocked(userId);
}
@@ -14805,7 +14807,7 @@ public class PackageManagerService extends IPackageManager.Stub {
pw.println();
int count = mSettings.mPackages.size();
if (count == 0) {
pw.println("No domain preferred apps!");
pw.println("No applications!");
pw.println();
} else {
final String prefix = " ";
@@ -14814,45 +14816,41 @@ public class PackageManagerService extends IPackageManager.Stub {
pw.println("No domain preferred apps!");
pw.println();
} else {
pw.println("Domain preferred apps status:");
pw.println("App verification status:");
pw.println();
count = 0;
for (PackageSetting ps : allPackageSettings) {
IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
if (ivi == null || ivi.getPackageName() == null) continue;
pw.println(prefix + "Package Name: " + ivi.getPackageName());
pw.println(prefix + "Package: " + ivi.getPackageName());
pw.println(prefix + "Domains: " + ivi.getDomainsString());
pw.println(prefix + "Status: " + ivi.getStatusString());
pw.println(prefix + "Status: " + ivi.getStatusString());
pw.println();
count++;
}
if (count == 0) {
pw.println(prefix + "No domain preferred app status!");
pw.println(prefix + "No app verification established.");
pw.println();
}
for (int userId : sUserManager.getUserIds()) {
pw.println("Domain preferred apps for User " + userId + ":");
pw.println("App linkages for user " + userId + ":");
pw.println();
count = 0;
for (PackageSetting ps : allPackageSettings) {
IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
if (ivi == null || ivi.getPackageName() == null) {
continue;
}
final int status = ps.getDomainVerificationStatusForUser(userId);
if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
continue;
}
pw.println(prefix + "Package Name: " + ivi.getPackageName());
pw.println(prefix + "Domains: " + ivi.getDomainsString());
pw.println(prefix + "Package: " + ps.name);
pw.println(prefix + "Domains: " + dumpDomainString(ps.name));
String statusStr = IntentFilterVerificationInfo.
getStatusStringFromValue(status);
pw.println(prefix + "Status: " + statusStr);
pw.println(prefix + "Status: " + statusStr);
pw.println();
count++;
}
if (count == 0) {
pw.println(prefix + "No domain preferred apps!");
pw.println(prefix + "No configured app linkages.");
pw.println();
}
}
@@ -14974,6 +14972,35 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
private String dumpDomainString(String packageName) {
List<IntentFilterVerificationInfo> iviList = getIntentFilterVerifications(packageName);
List<IntentFilter> filters = getAllIntentFilters(packageName);
ArraySet<String> result = new ArraySet<>();
if (iviList.size() > 0) {
for (IntentFilterVerificationInfo ivi : iviList) {
for (String host : ivi.getDomains()) {
result.add(host);
}
}
}
if (filters != null && filters.size() > 0) {
for (IntentFilter filter : filters) {
if (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
filter.hasDataScheme(IntentFilter.SCHEME_HTTPS)) {
result.addAll(filter.getHostsList());
}
}
}
StringBuilder sb = new StringBuilder(result.size() * 16);
for (String domain : result) {
if (sb.length() > 0) sb.append(" ");
sb.append(domain);
}
return sb.toString();
}
// ------- apps on sdcard specific code -------
static final boolean DEBUG_SD_INSTALL = false;
@@ -15893,6 +15920,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mInstaller.createUserConfig(userHandle);
mSettings.createNewUserLILPw(this, mInstaller, userHandle);
applyFactoryDefaultBrowserLPw(userHandle);
primeDomainVerificationsLPw(userHandle);
}
}