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