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