• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.pm.verify.domain;
18 
19 import android.annotation.CheckResult;
20 import android.annotation.NonNull;
21 import android.content.Intent;
22 import android.content.pm.ApplicationInfo;
23 import android.content.pm.PackageManager;
24 import android.content.pm.PackageManager.NameNotFoundException;
25 import android.text.TextUtils;
26 import android.util.Patterns;
27 
28 import com.android.internal.util.CollectionUtils;
29 import com.android.server.compat.PlatformCompat;
30 import com.android.server.pm.PackageManagerService;
31 import com.android.server.pm.parsing.pkg.AndroidPackage;
32 
33 import java.util.Set;
34 import java.util.regex.Matcher;
35 
36 public final class DomainVerificationUtils {
37 
38     private static final ThreadLocal<Matcher> sCachedMatcher = ThreadLocal.withInitial(
39             () -> Patterns.DOMAIN_NAME.matcher(""));
40 
41     /**
42      * Consolidates package exception messages. A generic unavailable message is included since the
43      * caller doesn't bother to check why the package isn't available.
44      */
45     @CheckResult
throwPackageUnavailable(@onNull String packageName)46     static NameNotFoundException throwPackageUnavailable(@NonNull String packageName)
47             throws NameNotFoundException {
48         throw new NameNotFoundException("Package " + packageName + " unavailable");
49     }
50 
isDomainVerificationIntent(Intent intent, @PackageManager.ResolveInfoFlags int resolveInfoFlags)51     public static boolean isDomainVerificationIntent(Intent intent,
52             @PackageManager.ResolveInfoFlags int resolveInfoFlags) {
53         if (!intent.isWebIntent()) {
54             return false;
55         }
56 
57         String host = intent.getData().getHost();
58         if (TextUtils.isEmpty(host)) {
59             return false;
60         }
61 
62         if (!sCachedMatcher.get().reset(host).matches()) {
63             return false;
64         }
65 
66         Set<String> categories = intent.getCategories();
67         int categoriesSize = CollectionUtils.size(categories);
68         if (categoriesSize > 2) {
69             // Specifying at least one non-app-link category
70             return false;
71         } else if (categoriesSize == 2) {
72             // Check for explicit app link intent with exactly BROWSABLE && DEFAULT
73             return intent.hasCategory(Intent.CATEGORY_DEFAULT)
74                     && intent.hasCategory(Intent.CATEGORY_BROWSABLE);
75         }
76 
77         boolean matchDefaultByFlags = (resolveInfoFlags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
78 
79         // Check if matches (BROWSABLE || none) && DEFAULT
80         if (categoriesSize == 0) {
81             // No categories, only allow matching DEFAULT by flags
82             return matchDefaultByFlags;
83         } else if (intent.hasCategory(Intent.CATEGORY_BROWSABLE)) {
84             // Intent matches BROWSABLE, must match DEFAULT by flags
85             return matchDefaultByFlags;
86         } else {
87             // Otherwise only needs to have DEFAULT
88             return intent.hasCategory(Intent.CATEGORY_DEFAULT);
89         }
90     }
91 
isChangeEnabled(PlatformCompat platformCompat, AndroidPackage pkg, long changeId)92     static boolean isChangeEnabled(PlatformCompat platformCompat, AndroidPackage pkg,
93             long changeId) {
94         return  platformCompat.isChangeEnabledInternalNoLogging(changeId, buildMockAppInfo(pkg));
95     }
96 
97     /**
98      * Passed to {@link PlatformCompat} because this can be invoked mid-install process or when
99      * {@link PackageManagerService#mLock} is being held, and {@link PlatformCompat} will not be
100      * able to query the pending {@link ApplicationInfo} from {@link PackageManager}.
101      * <p>
102      * TODO(b/177613575): Can a different API be used?
103      */
104     @NonNull
buildMockAppInfo(@onNull AndroidPackage pkg)105     private static ApplicationInfo buildMockAppInfo(@NonNull AndroidPackage pkg) {
106         ApplicationInfo appInfo = new ApplicationInfo();
107         appInfo.packageName = pkg.getPackageName();
108         appInfo.targetSdkVersion = pkg.getTargetSdkVersion();
109         return appInfo;
110     }
111 }
112