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