• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.internal.pm.pkg.component;
18 
19 import static com.android.internal.pm.pkg.component.ComponentParseUtils.flag;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.content.IntentFilter;
24 import android.content.pm.ActivityInfo;
25 import android.content.pm.ServiceInfo;
26 import android.content.pm.parsing.result.ParseInput;
27 import android.content.pm.parsing.result.ParseInput.DeferredError;
28 import android.content.pm.parsing.result.ParseResult;
29 import android.content.res.Resources;
30 import android.content.res.TypedArray;
31 import android.content.res.XmlResourceParser;
32 import android.multiuser.Flags;
33 import android.os.Build;
34 
35 import com.android.internal.R;
36 import com.android.internal.pm.pkg.parsing.ParsingPackage;
37 import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
38 import com.android.internal.pm.pkg.parsing.ParsingUtils;
39 
40 import org.xmlpull.v1.XmlPullParser;
41 import org.xmlpull.v1.XmlPullParserException;
42 
43 import java.io.IOException;
44 import java.util.Objects;
45 
46 /** @hide */
47 public class ParsedServiceUtils {
48 
49     @NonNull
parseService(String[] separateProcesses, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, boolean useRoundIcon, @Nullable String defaultSplitName, @NonNull ParseInput input)50     public static ParseResult<ParsedService> parseService(String[] separateProcesses,
51             ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
52             boolean useRoundIcon, @Nullable String defaultSplitName, @NonNull ParseInput input)
53             throws XmlPullParserException, IOException {
54         boolean visibleToEphemeral;
55         boolean setExported;
56 
57         final String packageName = pkg.getPackageName();
58         final ParsedServiceImpl service = new ParsedServiceImpl();
59         String tag = parser.getName();
60 
61         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestService);
62         try {
63             ParseResult<ParsedServiceImpl> result = ParsedMainComponentUtils.parseMainComponent(
64                     service, tag, separateProcesses, pkg, sa, flags, useRoundIcon, defaultSplitName,
65                     input,
66                     R.styleable.AndroidManifestService_banner,
67                     R.styleable.AndroidManifestService_description,
68                     R.styleable.AndroidManifestService_directBootAware,
69                     R.styleable.AndroidManifestService_enabled,
70                     R.styleable.AndroidManifestService_icon,
71                     R.styleable.AndroidManifestService_label,
72                     R.styleable.AndroidManifestService_logo,
73                     R.styleable.AndroidManifestService_name,
74                     R.styleable.AndroidManifestService_process,
75                     R.styleable.AndroidManifestService_roundIcon,
76                     R.styleable.AndroidManifestService_splitName,
77                     R.styleable.AndroidManifestService_attributionTags,
78                     R.styleable.AndroidManifestService_intentMatchingFlags
79             );
80 
81             if (result.isError()) {
82                 return input.error(result);
83             }
84 
85             setExported = sa.hasValue(R.styleable.AndroidManifestService_exported);
86             if (setExported) {
87                 service.setExported(sa.getBoolean(R.styleable.AndroidManifestService_exported,
88                         false));
89             }
90 
91             String permission = sa.getNonConfigurationString(
92                     R.styleable.AndroidManifestService_permission, 0);
93             service.setPermission(permission != null ? permission : pkg.getPermission());
94 
95             service.setForegroundServiceType(sa.getInt(
96                     R.styleable.AndroidManifestService_foregroundServiceType,
97                     ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE))
98                     .setFlags(service.getFlags() | (flag(ServiceInfo.FLAG_STOP_WITH_TASK,
99                             R.styleable.AndroidManifestService_stopWithTask, sa)
100                             | flag(ServiceInfo.FLAG_ISOLATED_PROCESS,
101                             R.styleable.AndroidManifestService_isolatedProcess, sa)
102                             | flag(ServiceInfo.FLAG_EXTERNAL_SERVICE,
103                             R.styleable.AndroidManifestService_externalService, sa)
104                             | flag(ServiceInfo.FLAG_USE_APP_ZYGOTE,
105                             R.styleable.AndroidManifestService_useAppZygote, sa)
106                             | flag(ServiceInfo.FLAG_ALLOW_SHARED_ISOLATED_PROCESS,
107                             R.styleable.AndroidManifestService_allowSharedIsolatedProcess, sa)
108                             | flag(ServiceInfo.FLAG_SINGLE_USER,
109                             R.styleable.AndroidManifestService_singleUser, sa)));
110 
111             if (Flags.enableSystemUserOnlyForServicesAndProviders()) {
112                 service.setFlags(service.getFlags() | flag(ServiceInfo.FLAG_SYSTEM_USER_ONLY,
113                         R.styleable.AndroidManifestService_systemUserOnly, sa));
114             }
115 
116             visibleToEphemeral = sa.getBoolean(
117                     R.styleable.AndroidManifestService_visibleToInstantApps, false);
118             if (visibleToEphemeral) {
119                 service.setFlags(service.getFlags() | ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP);
120                 pkg.setVisibleToInstantApps(true);
121             }
122         } finally {
123             sa.recycle();
124         }
125 
126         if (pkg.isSaveStateDisallowed()) {
127             // A heavy-weight application can not have services in its main process
128             // We can do direct compare because we intern all strings.
129             if (Objects.equals(service.getProcessName(), packageName)) {
130                 return input.error("Heavy-weight applications can not have services "
131                         + "in main process");
132             }
133         }
134         final int depth = parser.getDepth();
135         int type;
136         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
137                 && (type != XmlPullParser.END_TAG
138                 || parser.getDepth() > depth)) {
139             if (type != XmlPullParser.START_TAG) {
140                 continue;
141             }
142             if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
143                 continue;
144             }
145 
146             final ParseResult parseResult;
147             switch (parser.getName()) {
148                 case "intent-filter":
149                     ParseResult<ParsedIntentInfoImpl> intentResult = ParsedMainComponentUtils
150                             .parseIntentFilter(service, pkg, res, parser, visibleToEphemeral,
151                                     true /*allowGlobs*/, false /*allowAutoVerify*/,
152                                     false /*allowImplicitEphemeralVisibility*/,
153                                     false /*failOnNoActions*/, input);
154                     parseResult = intentResult;
155                     if (intentResult.isSuccess()) {
156                         ParsedIntentInfoImpl intent = intentResult.getResult();
157                         IntentFilter intentFilter = intent.getIntentFilter();
158                         service.setOrder(Math.max(intentFilter.getOrder(), service.getOrder()));
159                         service.addIntent(intent);
160                     }
161                     break;
162                 case "meta-data":
163                     parseResult = ParsedComponentUtils.addMetaData(service, pkg, res, parser, input);
164                     break;
165                 case "property":
166                     parseResult =
167                             ParsedComponentUtils.addProperty(service, pkg, res, parser, input);
168                     break;
169                 default:
170                     parseResult = ParsingUtils.unknownTag(tag, pkg, parser, input);
171                     break;
172             }
173 
174             if (parseResult.isError()) {
175                 return input.error(parseResult);
176             }
177         }
178 
179         if (!setExported) {
180             boolean hasIntentFilters = service.getIntents().size() > 0;
181             if (hasIntentFilters) {
182                 final ParseResult exportedCheckResult = input.deferError(
183                         service.getName() + ": Targeting S+ (version " + Build.VERSION_CODES.S
184                         + " and above) requires that an explicit value for android:exported be"
185                         + " defined when intent filters are present",
186                         DeferredError.MISSING_EXPORTED_FLAG);
187                 if (exportedCheckResult.isError()) {
188                     return input.error(exportedCheckResult);
189                 }
190             }
191             service.setExported(hasIntentFilters);
192         }
193 
194         return input.success(service);
195     }
196 }
197