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