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