Loading services/core/java/com/android/server/pm/PackageManagerService.java +83 −33 Original line number Diff line number Diff line Loading @@ -1346,13 +1346,6 @@ public class PackageManagerService extends IPackageManager.Stub int updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; boolean needUpdate = false; if (DEBUG_DOMAIN_VERIFICATION) { Slog.d(TAG, "Updating IntentFilterVerificationInfo for package " + packageName + " verificationId:" + verificationId + " verified=" + verified); } // In a success case, we promote from undefined or ASK to ALWAYS. This // supports a flow where the app fails validation but then ships an updated // APK that passes, and therefore deserves to be in ALWAYS. Loading Loading @@ -17639,65 +17632,120 @@ public class PackageManagerService extends IPackageManager.Stub + " Activities needs verification ..."); int count = 0; boolean handlesWebUris = false; ArraySet<String> domains = new ArraySet<>(); final boolean previouslyVerified; boolean hostSetExpanded = false; boolean needToRunVerify = false; synchronized (mLock) { // If this is a new install and we see that we've already run verification for this // package, we have nothing to do: it means the state was restored from backup. if (!replacing) { IntentFilterVerificationInfo ivi = mSettings.getIntentFilterVerificationLPr(packageName); if (ivi != null) { previouslyVerified = (ivi != null); if (!replacing && previouslyVerified) { if (DEBUG_DOMAIN_VERIFICATION) { Slog.i(TAG, "Package " + packageName + " already verified: status=" + ivi.getStatusString()); } return; } if (DEBUG_DOMAIN_VERIFICATION) { Slog.i(TAG, " Previous verified hosts: " + (ivi == null ? "[none]" : ivi.getDomainsString())); } // If any filters need to be verified, then all need to be. boolean needToVerify = false; // If any filters need to be verified, then all need to be. In addition, we need to // know whether an updating app has any web navigation intent filters, to re- // examine handling policy even if not re-verifying. final boolean needsVerification = needsNetworkVerificationLPr(packageName); for (ParsedActivity a : activities) { for (ParsedIntentInfo filter : a.getIntents()) { if (filter.needsVerification() && needsNetworkVerificationLPr(a.getPackageName())) { if (filter.handlesWebUris(true)) { handlesWebUris = true; } if (needsVerification && filter.needsVerification()) { if (DEBUG_DOMAIN_VERIFICATION) { Slog.d(TAG, "Intent filter needs verification, so processing all filters"); Slog.d(TAG, "autoVerify requested, processing all filters"); } needToVerify = true; needToRunVerify = true; // It's safe to break out here because filter.needsVerification() // can only be true if filter.handlesWebUris(true) returned true, so // we've already noted that. break; } } } if (needToVerify) { final boolean needsVerification = needsNetworkVerificationLPr(packageName); // Compare the new set of recognized hosts if the app is either requesting // autoVerify or has previously used autoVerify but no longer does. if (needToRunVerify || previouslyVerified) { final int verificationId = mIntentFilterVerificationToken++; for (ParsedActivity a : activities) { for (ParsedIntentInfo filter : a.getIntents()) { // Run verification against hosts mentioned in any web-nav intent filter, // even if the filter matches non-web schemes as well if (needsVerification && filter.handlesWebUris(false)) { if (filter.handlesWebUris(false /*onlyWebSchemes*/)) { if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Verification needed for IntentFilter:" + filter.toString()); mIntentFilterVerifier.addOneIntentFilterVerification( verifierUid, userId, verificationId, filter, packageName); domains.addAll(filter.getHostsList()); count++; } } } } if (DEBUG_DOMAIN_VERIFICATION) { Slog.i(TAG, " Update published hosts: " + domains.toString()); } // If we've previously verified this same host set (or a subset), we can trust that // a current ALWAYS policy is still applicable. If this is the case, we're done. // (If we aren't in ALWAYS, we want to reverify to allow for apps that had failing // hosts in their intent filters, then pushed a new apk that removed them and now // passes.) // // Cases: // + still autoVerify (needToRunVerify): // - preserve current state if all of: unexpanded, in always // - otherwise rerun as usual (fall through) // + no longer autoVerify (alreadyVerified && !needToRunVerify) // - wipe verification history always // - preserve current state if all of: unexpanded, in always hostSetExpanded = !previouslyVerified || (ivi != null && !ivi.getDomains().containsAll(domains)); final int currentPolicy = mSettings.getIntentFilterVerificationStatusLPr(packageName, userId); final boolean keepCurState = !hostSetExpanded && currentPolicy == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; if (needToRunVerify && keepCurState) { if (DEBUG_DOMAIN_VERIFICATION) { Slog.i(TAG, "Host set not expanding + ALWAYS -> no need to reverify"); } ivi.setDomains(domains); scheduleWriteSettingsLocked(); return; } else if (previouslyVerified && !needToRunVerify) { // Prior autoVerify state but not requesting it now. Clear autoVerify history, // and preserve the always policy iff the host set is not expanding. clearIntentFilterVerificationsLPw(packageName, userId, !keepCurState); return; } } if (count > 0) { if (needToRunVerify && count > 0) { // app requested autoVerify and has at least one matching intent filter if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Starting " + count + " IntentFilter verification" + (count > 1 ? "s" : "") + " for userId:" + userId); mIntentFilterVerifier.startVerifications(userId); } else { if (DEBUG_DOMAIN_VERIFICATION) { Slog.d(TAG, "No filters or not all autoVerify for " + packageName); Slog.d(TAG, "No web filters or no new host policy for " + packageName); } } } Loading Loading @@ -18402,7 +18450,7 @@ public class PackageManagerService extends IPackageManager.Stub if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { final SparseBooleanArray changedUsers = new SparseBooleanArray(); synchronized (mLock) { clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL); clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL, true); clearDefaultBrowserIfNeeded(packageName); mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName); removedAppId = mSettings.removePackageLPw(packageName); Loading Loading @@ -19495,13 +19543,14 @@ public class PackageManagerService extends IPackageManager.Stub final int packageCount = mPackages.size(); for (int i = 0; i < packageCount; i++) { AndroidPackage pkg = mPackages.valueAt(i); clearIntentFilterVerificationsLPw(pkg.getPackageName(), userId); clearIntentFilterVerificationsLPw(pkg.getPackageName(), userId, true); } } /** This method takes a specific user id as well as UserHandle.USER_ALL. */ @GuardedBy("mLock") void clearIntentFilterVerificationsLPw(String packageName, int userId) { void clearIntentFilterVerificationsLPw(String packageName, int userId, boolean alsoResetStatus) { if (userId == UserHandle.USER_ALL) { if (mSettings.removeIntentFilterVerificationLPw(packageName, mUserManager.getUserIds())) { Loading @@ -19510,7 +19559,8 @@ public class PackageManagerService extends IPackageManager.Stub } } } else { if (mSettings.removeIntentFilterVerificationLPw(packageName, userId)) { if (mSettings.removeIntentFilterVerificationLPw(packageName, userId, alsoResetStatus)) { scheduleWritePackageRestrictionsLocked(userId); } } services/core/java/com/android/server/pm/Settings.java +7 −3 Original line number Diff line number Diff line Loading @@ -1282,7 +1282,8 @@ public final class Settings { return result; } boolean removeIntentFilterVerificationLPw(String packageName, int userId) { boolean removeIntentFilterVerificationLPw(String packageName, int userId, boolean alsoResetStatus) { PackageSetting ps = mPackages.get(packageName); if (ps == null) { if (DEBUG_DOMAIN_VERIFICATION) { Loading @@ -1290,14 +1291,17 @@ public final class Settings { } return false; } if (alsoResetStatus) { ps.clearDomainVerificationStatusForUser(userId); } ps.setIntentFilterVerificationInfo(null); return true; } boolean removeIntentFilterVerificationLPw(String packageName, int[] userIds) { boolean result = false; for (int userId : userIds) { result |= removeIntentFilterVerificationLPw(packageName, userId); result |= removeIntentFilterVerificationLPw(packageName, userId, true); } return result; } Loading tests/AutoVerify/app1/Android.bp 0 → 100644 +11 −0 Original line number Diff line number Diff line android_app { name: "AutoVerifyTest", srcs: ["src/**/*.java"], resource_dirs: ["res"], platform_apis: true, min_sdk_version: "26", target_sdk_version: "26", optimize: { enabled: false, }, } tests/AutoVerify/app1/AndroidManifest.xml 0 → 100644 +43 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2020 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.test.autoverify" > <uses-sdk android:targetSdkVersion="26" /> <application android:label="@string/app_name" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter android:autoVerify="true"> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="http" /> <data android:scheme="https" /> <data android:host="explicit.example.com" /> </intent-filter> </activity> </application> </manifest> tests/AutoVerify/app1/res/values/strings.xml 0 → 100644 +21 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright 2020 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <resources> <!-- app icon label, do not translate --> <string name="app_name" translatable="false">AutoVerify Test</string> </resources> Loading
services/core/java/com/android/server/pm/PackageManagerService.java +83 −33 Original line number Diff line number Diff line Loading @@ -1346,13 +1346,6 @@ public class PackageManagerService extends IPackageManager.Stub int updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; boolean needUpdate = false; if (DEBUG_DOMAIN_VERIFICATION) { Slog.d(TAG, "Updating IntentFilterVerificationInfo for package " + packageName + " verificationId:" + verificationId + " verified=" + verified); } // In a success case, we promote from undefined or ASK to ALWAYS. This // supports a flow where the app fails validation but then ships an updated // APK that passes, and therefore deserves to be in ALWAYS. Loading Loading @@ -17639,65 +17632,120 @@ public class PackageManagerService extends IPackageManager.Stub + " Activities needs verification ..."); int count = 0; boolean handlesWebUris = false; ArraySet<String> domains = new ArraySet<>(); final boolean previouslyVerified; boolean hostSetExpanded = false; boolean needToRunVerify = false; synchronized (mLock) { // If this is a new install and we see that we've already run verification for this // package, we have nothing to do: it means the state was restored from backup. if (!replacing) { IntentFilterVerificationInfo ivi = mSettings.getIntentFilterVerificationLPr(packageName); if (ivi != null) { previouslyVerified = (ivi != null); if (!replacing && previouslyVerified) { if (DEBUG_DOMAIN_VERIFICATION) { Slog.i(TAG, "Package " + packageName + " already verified: status=" + ivi.getStatusString()); } return; } if (DEBUG_DOMAIN_VERIFICATION) { Slog.i(TAG, " Previous verified hosts: " + (ivi == null ? "[none]" : ivi.getDomainsString())); } // If any filters need to be verified, then all need to be. boolean needToVerify = false; // If any filters need to be verified, then all need to be. In addition, we need to // know whether an updating app has any web navigation intent filters, to re- // examine handling policy even if not re-verifying. final boolean needsVerification = needsNetworkVerificationLPr(packageName); for (ParsedActivity a : activities) { for (ParsedIntentInfo filter : a.getIntents()) { if (filter.needsVerification() && needsNetworkVerificationLPr(a.getPackageName())) { if (filter.handlesWebUris(true)) { handlesWebUris = true; } if (needsVerification && filter.needsVerification()) { if (DEBUG_DOMAIN_VERIFICATION) { Slog.d(TAG, "Intent filter needs verification, so processing all filters"); Slog.d(TAG, "autoVerify requested, processing all filters"); } needToVerify = true; needToRunVerify = true; // It's safe to break out here because filter.needsVerification() // can only be true if filter.handlesWebUris(true) returned true, so // we've already noted that. break; } } } if (needToVerify) { final boolean needsVerification = needsNetworkVerificationLPr(packageName); // Compare the new set of recognized hosts if the app is either requesting // autoVerify or has previously used autoVerify but no longer does. if (needToRunVerify || previouslyVerified) { final int verificationId = mIntentFilterVerificationToken++; for (ParsedActivity a : activities) { for (ParsedIntentInfo filter : a.getIntents()) { // Run verification against hosts mentioned in any web-nav intent filter, // even if the filter matches non-web schemes as well if (needsVerification && filter.handlesWebUris(false)) { if (filter.handlesWebUris(false /*onlyWebSchemes*/)) { if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Verification needed for IntentFilter:" + filter.toString()); mIntentFilterVerifier.addOneIntentFilterVerification( verifierUid, userId, verificationId, filter, packageName); domains.addAll(filter.getHostsList()); count++; } } } } if (DEBUG_DOMAIN_VERIFICATION) { Slog.i(TAG, " Update published hosts: " + domains.toString()); } // If we've previously verified this same host set (or a subset), we can trust that // a current ALWAYS policy is still applicable. If this is the case, we're done. // (If we aren't in ALWAYS, we want to reverify to allow for apps that had failing // hosts in their intent filters, then pushed a new apk that removed them and now // passes.) // // Cases: // + still autoVerify (needToRunVerify): // - preserve current state if all of: unexpanded, in always // - otherwise rerun as usual (fall through) // + no longer autoVerify (alreadyVerified && !needToRunVerify) // - wipe verification history always // - preserve current state if all of: unexpanded, in always hostSetExpanded = !previouslyVerified || (ivi != null && !ivi.getDomains().containsAll(domains)); final int currentPolicy = mSettings.getIntentFilterVerificationStatusLPr(packageName, userId); final boolean keepCurState = !hostSetExpanded && currentPolicy == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; if (needToRunVerify && keepCurState) { if (DEBUG_DOMAIN_VERIFICATION) { Slog.i(TAG, "Host set not expanding + ALWAYS -> no need to reverify"); } ivi.setDomains(domains); scheduleWriteSettingsLocked(); return; } else if (previouslyVerified && !needToRunVerify) { // Prior autoVerify state but not requesting it now. Clear autoVerify history, // and preserve the always policy iff the host set is not expanding. clearIntentFilterVerificationsLPw(packageName, userId, !keepCurState); return; } } if (count > 0) { if (needToRunVerify && count > 0) { // app requested autoVerify and has at least one matching intent filter if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Starting " + count + " IntentFilter verification" + (count > 1 ? "s" : "") + " for userId:" + userId); mIntentFilterVerifier.startVerifications(userId); } else { if (DEBUG_DOMAIN_VERIFICATION) { Slog.d(TAG, "No filters or not all autoVerify for " + packageName); Slog.d(TAG, "No web filters or no new host policy for " + packageName); } } } Loading Loading @@ -18402,7 +18450,7 @@ public class PackageManagerService extends IPackageManager.Stub if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { final SparseBooleanArray changedUsers = new SparseBooleanArray(); synchronized (mLock) { clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL); clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL, true); clearDefaultBrowserIfNeeded(packageName); mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName); removedAppId = mSettings.removePackageLPw(packageName); Loading Loading @@ -19495,13 +19543,14 @@ public class PackageManagerService extends IPackageManager.Stub final int packageCount = mPackages.size(); for (int i = 0; i < packageCount; i++) { AndroidPackage pkg = mPackages.valueAt(i); clearIntentFilterVerificationsLPw(pkg.getPackageName(), userId); clearIntentFilterVerificationsLPw(pkg.getPackageName(), userId, true); } } /** This method takes a specific user id as well as UserHandle.USER_ALL. */ @GuardedBy("mLock") void clearIntentFilterVerificationsLPw(String packageName, int userId) { void clearIntentFilterVerificationsLPw(String packageName, int userId, boolean alsoResetStatus) { if (userId == UserHandle.USER_ALL) { if (mSettings.removeIntentFilterVerificationLPw(packageName, mUserManager.getUserIds())) { Loading @@ -19510,7 +19559,8 @@ public class PackageManagerService extends IPackageManager.Stub } } } else { if (mSettings.removeIntentFilterVerificationLPw(packageName, userId)) { if (mSettings.removeIntentFilterVerificationLPw(packageName, userId, alsoResetStatus)) { scheduleWritePackageRestrictionsLocked(userId); } }
services/core/java/com/android/server/pm/Settings.java +7 −3 Original line number Diff line number Diff line Loading @@ -1282,7 +1282,8 @@ public final class Settings { return result; } boolean removeIntentFilterVerificationLPw(String packageName, int userId) { boolean removeIntentFilterVerificationLPw(String packageName, int userId, boolean alsoResetStatus) { PackageSetting ps = mPackages.get(packageName); if (ps == null) { if (DEBUG_DOMAIN_VERIFICATION) { Loading @@ -1290,14 +1291,17 @@ public final class Settings { } return false; } if (alsoResetStatus) { ps.clearDomainVerificationStatusForUser(userId); } ps.setIntentFilterVerificationInfo(null); return true; } boolean removeIntentFilterVerificationLPw(String packageName, int[] userIds) { boolean result = false; for (int userId : userIds) { result |= removeIntentFilterVerificationLPw(packageName, userId); result |= removeIntentFilterVerificationLPw(packageName, userId, true); } return result; } Loading
tests/AutoVerify/app1/Android.bp 0 → 100644 +11 −0 Original line number Diff line number Diff line android_app { name: "AutoVerifyTest", srcs: ["src/**/*.java"], resource_dirs: ["res"], platform_apis: true, min_sdk_version: "26", target_sdk_version: "26", optimize: { enabled: false, }, }
tests/AutoVerify/app1/AndroidManifest.xml 0 → 100644 +43 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2020 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.test.autoverify" > <uses-sdk android:targetSdkVersion="26" /> <application android:label="@string/app_name" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter android:autoVerify="true"> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="http" /> <data android:scheme="https" /> <data android:host="explicit.example.com" /> </intent-filter> </activity> </application> </manifest>
tests/AutoVerify/app1/res/values/strings.xml 0 → 100644 +21 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright 2020 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <resources> <!-- app icon label, do not translate --> <string name="app_name" translatable="false">AutoVerify Test</string> </resources>