1 /* 2 * Copyright (C) 2015 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 package com.android.car.pm; 17 18 import android.content.Context; 19 import android.content.pm.ApplicationInfo; 20 import android.content.pm.PackageManager; 21 import android.content.pm.PackageManager.NameNotFoundException; 22 import android.content.res.Resources; 23 import android.content.res.XmlResourceParser; 24 import android.util.Log; 25 26 import com.android.car.CarLog; 27 28 import java.io.IOException; 29 import java.util.LinkedList; 30 31 import org.xmlpull.v1.XmlPullParser; 32 import org.xmlpull.v1.XmlPullParserException; 33 34 /** 35 * Read meta data containing car application info. 36 */ 37 public class CarAppMetadataReader { 38 /** Name of the meta-data attribute for the automotive application XML resource */ 39 private static final String METADATA_ATTRIBUTE = "android.car.application"; 40 /** Name of the tag to declare automotive usage */ 41 private static final String USES_TAG = "uses"; 42 /** Name of the attribute to name the usage type */ 43 private static final String NAME_ATTRIBUTE = "name"; 44 45 private static final String NAME_ATTR_TYPE_SERVICE = "service"; 46 private static final String NAME_ATTR_TYPE_ACTIVITY = "activity"; 47 48 private static final String CLASS_ATTRIBUTE = "class"; 49 50 public static CarAppMetadataInfo parseMetadata(Context context, String packageName) { 51 int metadataId = 0; 52 Resources resources = null; 53 try { 54 metadataId = getMetadataId(context, packageName); 55 if (metadataId == 0) { 56 return null; 57 } 58 PackageManager pm = context.getPackageManager(); 59 resources = pm.getResourcesForApplication(packageName); 60 } catch (NameNotFoundException e) { 61 Log.w(CarLog.TAG_PACKAGE, "Cannot read mta data, package:" + packageName, e); 62 return null; 63 } 64 65 // Try to open the XML resource 66 XmlResourceParser parser = null; 67 try { 68 parser = resources.getXml(metadataId); 69 } catch (Resources.NotFoundException e) { 70 Log.w(CarLog.TAG_PACKAGE, "Resource not found [" + packageName + "]"); 71 return null; 72 } 73 74 boolean useService = false; 75 boolean useAllActivities = false; 76 LinkedList<String> activities = null; 77 // Now, read the XML and pull out the appropriate "uses" attribute 78 int eventType; 79 try { 80 eventType = parser.next(); 81 while (eventType != XmlPullParser.END_DOCUMENT) { 82 if (eventType == XmlPullParser.START_TAG && USES_TAG.equals(parser.getName())) { 83 String nameAttribute = 84 parser.getAttributeValue(null /*namespace*/, NAME_ATTRIBUTE); 85 if (nameAttribute == null) { 86 Log.w(CarLog.TAG_PACKAGE, "Bad metadata," + METADATA_ATTRIBUTE + 87 " for pavkage:" + packageName); 88 return null; 89 } 90 switch (nameAttribute) { 91 case NAME_ATTR_TYPE_SERVICE: 92 useService = true; 93 break; 94 case NAME_ATTR_TYPE_ACTIVITY: { 95 String classAttribute = parser.getAttributeValue(null /*namespace*/, 96 CLASS_ATTRIBUTE); 97 if (classAttribute == null) { // all activities 98 useAllActivities = true; 99 } else { 100 if (activities == null) { 101 activities = new LinkedList<>(); 102 } 103 activities.add(classAttribute); 104 } 105 } break; 106 } 107 } 108 eventType = parser.next(); 109 } 110 } catch (XmlPullParserException | IOException e) { 111 Log.w(CarLog.TAG_PACKAGE, "Resource not parsable [" + packageName + "]"); 112 return null; 113 } 114 String[] activityStrings = null; 115 if (activities != null) { 116 activityStrings = activities.toArray(new String[activities.size()]); 117 } 118 return new CarAppMetadataInfo(useService, useAllActivities, activityStrings); 119 } 120 121 /** 122 * Returns the resource ID of the automotive application XML 123 * @throws NameNotFoundException 124 */ 125 private static int getMetadataId(Context context, String packageName) 126 throws NameNotFoundException { 127 final PackageManager pm = context.getPackageManager(); 128 ApplicationInfo appInfo; 129 appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA /*flags*/); 130 if (appInfo.metaData == null) { 131 return 0; 132 } 133 return appInfo.metaData.getInt(METADATA_ATTRIBUTE, 0); 134 } 135 136 /** 137 * App's metada for car application 138 */ 139 public static class CarAppMetadataInfo { 140 /** has "service" use */ 141 public final boolean useService; 142 /** has "activity" use with no specific class */ 143 public final boolean useAllActivities; 144 /** has "activity" uses with specific classes */ 145 public String[] activities; 146 147 private CarAppMetadataInfo(boolean useService, boolean useAllActivities, 148 String[] activities) { 149 this.useService = useService; 150 this.useAllActivities = useAllActivities; 151 this.activities = activities; 152 } 153 } 154 } 155