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_ALLOW_SHARED_ISOLATED_PROCESS, 104 R.styleable.AndroidManifestService_allowSharedIsolatedProcess, sa) 105 | flag(ServiceInfo.FLAG_SINGLE_USER, 106 R.styleable.AndroidManifestService_singleUser, sa))); 107 108 visibleToEphemeral = sa.getBoolean( 109 R.styleable.AndroidManifestService_visibleToInstantApps, false); 110 if (visibleToEphemeral) { 111 service.setFlags(service.getFlags() | ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP); 112 pkg.setVisibleToInstantApps(true); 113 } 114 } finally { 115 sa.recycle(); 116 } 117 118 if (pkg.isSaveStateDisallowed()) { 119 // A heavy-weight application can not have services in its main process 120 // We can do direct compare because we intern all strings. 121 if (Objects.equals(service.getProcessName(), packageName)) { 122 return input.error("Heavy-weight applications can not have services " 123 + "in main process"); 124 } 125 } 126 final int depth = parser.getDepth(); 127 int type; 128 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 129 && (type != XmlPullParser.END_TAG 130 || parser.getDepth() > depth)) { 131 if (type != XmlPullParser.START_TAG) { 132 continue; 133 } 134 135 final ParseResult parseResult; 136 switch (parser.getName()) { 137 case "intent-filter": 138 ParseResult<ParsedIntentInfoImpl> intentResult = ParsedMainComponentUtils 139 .parseIntentFilter(service, pkg, res, parser, visibleToEphemeral, 140 true /*allowGlobs*/, false /*allowAutoVerify*/, 141 false /*allowImplicitEphemeralVisibility*/, 142 false /*failOnNoActions*/, input); 143 parseResult = intentResult; 144 if (intentResult.isSuccess()) { 145 ParsedIntentInfoImpl intent = intentResult.getResult(); 146 IntentFilter intentFilter = intent.getIntentFilter(); 147 service.setOrder(Math.max(intentFilter.getOrder(), service.getOrder())); 148 service.addIntent(intent); 149 } 150 break; 151 case "meta-data": 152 parseResult = ParsedComponentUtils.addMetaData(service, pkg, res, parser, input); 153 break; 154 case "property": 155 parseResult = 156 ParsedComponentUtils.addProperty(service, pkg, res, parser, input); 157 break; 158 default: 159 parseResult = ParsingUtils.unknownTag(tag, pkg, parser, input); 160 break; 161 } 162 163 if (parseResult.isError()) { 164 return input.error(parseResult); 165 } 166 } 167 168 if (!setExported) { 169 boolean hasIntentFilters = service.getIntents().size() > 0; 170 if (hasIntentFilters) { 171 final ParseResult exportedCheckResult = input.deferError( 172 service.getName() + ": Targeting S+ (version " + Build.VERSION_CODES.S 173 + " and above) requires that an explicit value for android:exported be" 174 + " defined when intent filters are present", 175 DeferredError.MISSING_EXPORTED_FLAG); 176 if (exportedCheckResult.isError()) { 177 return input.error(exportedCheckResult); 178 } 179 } 180 service.setExported(hasIntentFilters); 181 } 182 183 return input.success(service); 184 } 185 } 186