1 /*
2  * Copyright (C) 2022 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.pkg;
18 
19 import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
20 import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
21 
22 import android.annotation.NonNull;
23 import android.content.pm.ComponentInfo;
24 import android.content.pm.PackageManager;
25 import android.os.Debug;
26 import android.util.DebugUtils;
27 import android.util.Slog;
28 
29 import com.android.server.pm.pkg.component.ParsedMainComponent;
30 
31 /** @hide */
32 public class PackageUserStateUtils {
33 
34     private static final boolean DEBUG = false;
35     private static final String TAG = "PackageUserStateUtils";
36 
isMatch(@onNull PackageUserState state, ComponentInfo componentInfo, long flags)37     public static boolean isMatch(@NonNull PackageUserState state,
38             ComponentInfo componentInfo, long flags) {
39         return isMatch(state, componentInfo.applicationInfo.isSystemApp(),
40                 componentInfo.applicationInfo.enabled, componentInfo.enabled,
41                 componentInfo.directBootAware, componentInfo.name, flags);
42     }
43 
isMatch(@onNull PackageUserState state, boolean isSystem, boolean isPackageEnabled, ParsedMainComponent component, long flags)44     public static boolean isMatch(@NonNull PackageUserState state, boolean isSystem,
45             boolean isPackageEnabled, ParsedMainComponent component, long flags) {
46         return isMatch(state, isSystem, isPackageEnabled, component.isEnabled(),
47                 component.isDirectBootAware(), component.getName(), flags);
48     }
49 
50     /**
51      * Test if the given component is considered installed, enabled and a match for the given
52      * flags.
53      *
54      * <p>
55      * Expects at least one of {@link PackageManager#MATCH_DIRECT_BOOT_AWARE} and {@link
56      * PackageManager#MATCH_DIRECT_BOOT_UNAWARE} are specified in {@code flags}.
57      * </p>
58      */
isMatch(@onNull PackageUserState state, boolean isSystem, boolean isPackageEnabled, boolean isComponentEnabled, boolean isComponentDirectBootAware, String componentName, long flags)59     public static boolean isMatch(@NonNull PackageUserState state, boolean isSystem,
60             boolean isPackageEnabled, boolean isComponentEnabled,
61             boolean isComponentDirectBootAware, String componentName, long flags) {
62         final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0;
63         if (!isAvailable(state, flags) && !(isSystem && matchUninstalled)) {
64             return reportIfDebug(false, flags);
65         }
66 
67         if (!isEnabled(state, isPackageEnabled, isComponentEnabled, componentName, flags)) {
68             return reportIfDebug(false, flags);
69         }
70 
71         if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
72             if (!isSystem) {
73                 return reportIfDebug(false, flags);
74             }
75         }
76 
77         final boolean matchesUnaware = ((flags & PackageManager.MATCH_DIRECT_BOOT_UNAWARE) != 0)
78                 && !isComponentDirectBootAware;
79         final boolean matchesAware = ((flags & PackageManager.MATCH_DIRECT_BOOT_AWARE) != 0)
80                 && isComponentDirectBootAware;
81         return reportIfDebug(matchesUnaware || matchesAware, flags);
82     }
83 
isAvailable(@onNull PackageUserState state, long flags)84     public static boolean isAvailable(@NonNull PackageUserState state, long flags) {
85         // True if it is installed for this user and it is not hidden. If it is hidden,
86         // still return true if the caller requested MATCH_UNINSTALLED_PACKAGES
87         final boolean matchAnyUser = (flags & PackageManager.MATCH_ANY_USER) != 0;
88         final boolean matchUninstalled = (flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0;
89         return matchAnyUser
90                 || (state.isInstalled()
91                 && (!state.isHidden() || matchUninstalled));
92     }
93 
reportIfDebug(boolean result, long flags)94     public static boolean reportIfDebug(boolean result, long flags) {
95         if (DEBUG && !result) {
96             Slog.i(TAG, "No match!; flags: "
97                     + DebugUtils.flagsToString(PackageManager.class, "MATCH_", flags) + " "
98                     + Debug.getCaller());
99         }
100         return result;
101     }
102 
isEnabled(@onNull PackageUserState state, ComponentInfo componentInfo, long flags)103     public static boolean isEnabled(@NonNull PackageUserState state, ComponentInfo componentInfo,
104             long flags) {
105         return isEnabled(state, componentInfo.applicationInfo.enabled, componentInfo.enabled,
106                 componentInfo.name, flags);
107     }
108 
isEnabled(@onNull PackageUserState state, boolean isPackageEnabled, ParsedMainComponent parsedComponent, long flags)109     public static boolean isEnabled(@NonNull PackageUserState state, boolean isPackageEnabled,
110             ParsedMainComponent parsedComponent, long flags) {
111         return isEnabled(state, isPackageEnabled, parsedComponent.isEnabled(),
112                 parsedComponent.getName(), flags);
113     }
114 
115     /**
116      * Test if the given component is considered enabled.
117      */
isEnabled(@onNull PackageUserState state, boolean isPackageEnabled, boolean isComponentEnabled, String componentName, long flags)118     public static boolean isEnabled(@NonNull PackageUserState state,
119             boolean isPackageEnabled, boolean isComponentEnabled, String componentName,
120             long flags) {
121         if ((flags & MATCH_DISABLED_COMPONENTS) != 0) {
122             return true;
123         }
124 
125         // First check if the overall package is disabled; if the package is
126         // enabled then fall through to check specific component
127         switch (state.getEnabledState()) {
128             case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
129             case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
130                 return false;
131             case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
132                 if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) == 0) {
133                     return false;
134                 }
135                 // fallthrough
136             case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
137                 if (!isPackageEnabled) {
138                     return false;
139                 }
140                 // fallthrough
141             case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
142                 break;
143         }
144 
145         // Check if component has explicit state before falling through to
146         // the manifest default
147         if (state.isComponentEnabled(componentName)) {
148             return true;
149         } else if (state.isComponentDisabled(componentName)) {
150             return false;
151         }
152 
153         return isComponentEnabled;
154     }
155 
isPackageEnabled(@onNull PackageUserState state, @NonNull AndroidPackage pkg)156     public static boolean isPackageEnabled(@NonNull PackageUserState state,
157             @NonNull AndroidPackage pkg) {
158         switch (state.getEnabledState()) {
159             case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
160                 return true;
161             case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
162             case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
163             case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
164                 return false;
165             default:
166             case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
167                 return pkg.isEnabled();
168         }
169     }
170 }
171