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 ); 79 80 if (result.isError()) { 81 return input.error(result); 82 } 83 84 setExported = sa.hasValue(R.styleable.AndroidManifestService_exported); 85 if (setExported) { 86 service.setExported(sa.getBoolean(R.styleable.AndroidManifestService_exported, 87 false)); 88 } 89 90 String permission = sa.getNonConfigurationString( 91 R.styleable.AndroidManifestService_permission, 0); 92 service.setPermission(permission != null ? permission : pkg.getPermission()); 93 94 service.setForegroundServiceType(sa.getInt( 95 R.styleable.AndroidManifestService_foregroundServiceType, 96 ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE)) 97 .setFlags(service.getFlags() | (flag(ServiceInfo.FLAG_STOP_WITH_TASK, 98 R.styleable.AndroidManifestService_stopWithTask, sa) 99 | flag(ServiceInfo.FLAG_ISOLATED_PROCESS, 100 R.styleable.AndroidManifestService_isolatedProcess, sa) 101 | flag(ServiceInfo.FLAG_EXTERNAL_SERVICE, 102 R.styleable.AndroidManifestService_externalService, sa) 103 | flag(ServiceInfo.FLAG_USE_APP_ZYGOTE, 104 R.styleable.AndroidManifestService_useAppZygote, sa) 105 | flag(ServiceInfo.FLAG_ALLOW_SHARED_ISOLATED_PROCESS, 106 R.styleable.AndroidManifestService_allowSharedIsolatedProcess, sa) 107 | flag(ServiceInfo.FLAG_SINGLE_USER, 108 R.styleable.AndroidManifestService_singleUser, sa))); 109 110 if (Flags.enableSystemUserOnlyForServicesAndProviders()) { 111 service.setFlags(service.getFlags() | flag(ServiceInfo.FLAG_SYSTEM_USER_ONLY, 112 R.styleable.AndroidManifestService_systemUserOnly, sa)); 113 } 114 115 visibleToEphemeral = sa.getBoolean( 116 R.styleable.AndroidManifestService_visibleToInstantApps, false); 117 if (visibleToEphemeral) { 118 service.setFlags(service.getFlags() | ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP); 119 pkg.setVisibleToInstantApps(true); 120 } 121 } finally { 122 sa.recycle(); 123 } 124 125 if (pkg.isSaveStateDisallowed()) { 126 // A heavy-weight application can not have services in its main process 127 // We can do direct compare because we intern all strings. 128 if (Objects.equals(service.getProcessName(), packageName)) { 129 return input.error("Heavy-weight applications can not have services " 130 + "in main process"); 131 } 132 } 133 final int depth = parser.getDepth(); 134 int type; 135 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 136 && (type != XmlPullParser.END_TAG 137 || parser.getDepth() > depth)) { 138 if (type != XmlPullParser.START_TAG) { 139 continue; 140 } 141 if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) { 142 continue; 143 } 144 145 final ParseResult parseResult; 146 switch (parser.getName()) { 147 case "intent-filter": 148 ParseResult<ParsedIntentInfoImpl> intentResult = ParsedMainComponentUtils 149 .parseIntentFilter(service, pkg, res, parser, visibleToEphemeral, 150 true /*allowGlobs*/, false /*allowAutoVerify*/, 151 false /*allowImplicitEphemeralVisibility*/, 152 false /*failOnNoActions*/, input); 153 parseResult = intentResult; 154 if (intentResult.isSuccess()) { 155 ParsedIntentInfoImpl intent = intentResult.getResult(); 156 IntentFilter intentFilter = intent.getIntentFilter(); 157 service.setOrder(Math.max(intentFilter.getOrder(), service.getOrder())); 158 service.addIntent(intent); 159 } 160 break; 161 case "meta-data": 162 parseResult = ParsedComponentUtils.addMetaData(service, pkg, res, parser, input); 163 break; 164 case "property": 165 parseResult = 166 ParsedComponentUtils.addProperty(service, pkg, res, parser, input); 167 break; 168 default: 169 parseResult = ParsingUtils.unknownTag(tag, pkg, parser, input); 170 break; 171 } 172 173 if (parseResult.isError()) { 174 return input.error(parseResult); 175 } 176 } 177 178 if (!setExported) { 179 boolean hasIntentFilters = service.getIntents().size() > 0; 180 if (hasIntentFilters) { 181 final ParseResult exportedCheckResult = input.deferError( 182 service.getName() + ": Targeting S+ (version " + Build.VERSION_CODES.S 183 + " and above) requires that an explicit value for android:exported be" 184 + " defined when intent filters are present", 185 DeferredError.MISSING_EXPORTED_FLAG); 186 if (exportedCheckResult.isError()) { 187 return input.error(exportedCheckResult); 188 } 189 } 190 service.setExported(hasIntentFilters); 191 } 192 193 return input.success(service); 194 } 195 } 196