1 /* 2 * Copyright (C) 2011 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; 18 19 import android.content.pm.PackageManagerInternal; 20 import com.android.internal.util.XmlUtils; 21 22 import com.android.server.LocalServices; 23 import org.xmlpull.v1.XmlPullParser; 24 import org.xmlpull.v1.XmlPullParserException; 25 import org.xmlpull.v1.XmlSerializer; 26 27 import android.content.ComponentName; 28 import android.content.IntentFilter; 29 import android.content.pm.ActivityInfo; 30 import android.content.pm.ResolveInfo; 31 import android.util.Slog; 32 33 import java.io.IOException; 34 import java.io.PrintWriter; 35 import java.util.ArrayList; 36 import java.util.List; 37 38 public class PreferredComponent { 39 private static final String TAG_SET = "set"; 40 private static final String ATTR_ALWAYS = "always"; // boolean 41 private static final String ATTR_MATCH = "match"; // number 42 private static final String ATTR_NAME = "name"; // component name 43 private static final String ATTR_SET = "set"; // number 44 45 public final int mMatch; 46 public final ComponentName mComponent; 47 // Whether this is to be the one that's always chosen. If false, it's the most recently chosen. 48 public boolean mAlways; 49 50 final String[] mSetPackages; 51 final String[] mSetClasses; 52 final String[] mSetComponents; 53 final String mShortComponent; 54 private String mParseError; 55 56 private final Callbacks mCallbacks; 57 private final String mSetupWizardPackageName; 58 59 public interface Callbacks { onReadTag(String tagName, XmlPullParser parser)60 public boolean onReadTag(String tagName, XmlPullParser parser) 61 throws XmlPullParserException, IOException; 62 } 63 PreferredComponent(Callbacks callbacks, int match, ComponentName[] set, ComponentName component, boolean always)64 public PreferredComponent(Callbacks callbacks, int match, ComponentName[] set, 65 ComponentName component, boolean always) { 66 mCallbacks = callbacks; 67 mMatch = match&IntentFilter.MATCH_CATEGORY_MASK; 68 mComponent = component; 69 mAlways = always; 70 mShortComponent = component.flattenToShortString(); 71 mParseError = null; 72 mSetupWizardPackageName = null; 73 if (set != null) { 74 final int N = set.length; 75 String[] myPackages = new String[N]; 76 String[] myClasses = new String[N]; 77 String[] myComponents = new String[N]; 78 for (int i=0; i<N; i++) { 79 ComponentName cn = set[i]; 80 if (cn == null) { 81 mSetPackages = null; 82 mSetClasses = null; 83 mSetComponents = null; 84 return; 85 } 86 myPackages[i] = cn.getPackageName().intern(); 87 myClasses[i] = cn.getClassName().intern(); 88 myComponents[i] = cn.flattenToShortString(); 89 } 90 mSetPackages = myPackages; 91 mSetClasses = myClasses; 92 mSetComponents = myComponents; 93 } else { 94 mSetPackages = null; 95 mSetClasses = null; 96 mSetComponents = null; 97 } 98 } 99 PreferredComponent(Callbacks callbacks, XmlPullParser parser)100 public PreferredComponent(Callbacks callbacks, XmlPullParser parser) 101 throws XmlPullParserException, IOException { 102 mCallbacks = callbacks; 103 mShortComponent = parser.getAttributeValue(null, ATTR_NAME); 104 mComponent = ComponentName.unflattenFromString(mShortComponent); 105 if (mComponent == null) { 106 mParseError = "Bad activity name " + mShortComponent; 107 } 108 String matchStr = parser.getAttributeValue(null, ATTR_MATCH); 109 mMatch = matchStr != null ? Integer.parseInt(matchStr, 16) : 0; 110 String setCountStr = parser.getAttributeValue(null, ATTR_SET); 111 int setCount = setCountStr != null ? Integer.parseInt(setCountStr) : 0; 112 String alwaysStr = parser.getAttributeValue(null, ATTR_ALWAYS); 113 mAlways = alwaysStr != null ? Boolean.parseBoolean(alwaysStr) : true; 114 115 String[] myPackages = setCount > 0 ? new String[setCount] : null; 116 String[] myClasses = setCount > 0 ? new String[setCount] : null; 117 String[] myComponents = setCount > 0 ? new String[setCount] : null; 118 119 int setPos = 0; 120 121 int outerDepth = parser.getDepth(); 122 int type; 123 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 124 && (type != XmlPullParser.END_TAG 125 || parser.getDepth() > outerDepth)) { 126 if (type == XmlPullParser.END_TAG 127 || type == XmlPullParser.TEXT) { 128 continue; 129 } 130 131 String tagName = parser.getName(); 132 //Log.i(TAG, "Parse outerDepth=" + outerDepth + " depth=" 133 // + parser.getDepth() + " tag=" + tagName); 134 if (tagName.equals(TAG_SET)) { 135 String name = parser.getAttributeValue(null, ATTR_NAME); 136 if (name == null) { 137 if (mParseError == null) { 138 mParseError = "No name in set tag in preferred activity " 139 + mShortComponent; 140 } 141 } else if (setPos >= setCount) { 142 if (mParseError == null) { 143 mParseError = "Too many set tags in preferred activity " 144 + mShortComponent; 145 } 146 } else { 147 ComponentName cn = ComponentName.unflattenFromString(name); 148 if (cn == null) { 149 if (mParseError == null) { 150 mParseError = "Bad set name " + name + " in preferred activity " 151 + mShortComponent; 152 } 153 } else { 154 myPackages[setPos] = cn.getPackageName(); 155 myClasses[setPos] = cn.getClassName(); 156 myComponents[setPos] = name; 157 setPos++; 158 } 159 } 160 XmlUtils.skipCurrentTag(parser); 161 } else if (!mCallbacks.onReadTag(tagName, parser)) { 162 Slog.w("PreferredComponent", "Unknown element: " + parser.getName()); 163 XmlUtils.skipCurrentTag(parser); 164 } 165 } 166 167 if (setPos != setCount) { 168 if (mParseError == null) { 169 mParseError = "Not enough set tags (expected " + setCount 170 + " but found " + setPos + ") in " + mShortComponent; 171 } 172 } 173 174 mSetPackages = myPackages; 175 mSetClasses = myClasses; 176 mSetComponents = myComponents; 177 final PackageManagerInternal packageManagerInternal = LocalServices.getService(PackageManagerInternal.class); 178 mSetupWizardPackageName = packageManagerInternal.getSetupWizardPackageName(); 179 } 180 getParseError()181 public String getParseError() { 182 return mParseError; 183 } 184 writeToXml(XmlSerializer serializer, boolean full)185 public void writeToXml(XmlSerializer serializer, boolean full) throws IOException { 186 final int NS = mSetClasses != null ? mSetClasses.length : 0; 187 serializer.attribute(null, ATTR_NAME, mShortComponent); 188 if (full) { 189 if (mMatch != 0) { 190 serializer.attribute(null, ATTR_MATCH, Integer.toHexString(mMatch)); 191 } 192 serializer.attribute(null, ATTR_ALWAYS, Boolean.toString(mAlways)); 193 serializer.attribute(null, ATTR_SET, Integer.toString(NS)); 194 for (int s=0; s<NS; s++) { 195 serializer.startTag(null, TAG_SET); 196 serializer.attribute(null, ATTR_NAME, mSetComponents[s]); 197 serializer.endTag(null, TAG_SET); 198 } 199 } 200 } 201 sameSet(List<ResolveInfo> query, boolean excludeSetupWizardPackage)202 public boolean sameSet(List<ResolveInfo> query, boolean excludeSetupWizardPackage) { 203 if (mSetPackages == null) { 204 return query == null; 205 } 206 if (query == null) { 207 return false; 208 } 209 final int NQ = query.size(); 210 final int NS = mSetPackages.length; 211 int numMatch = 0; 212 for (int i=0; i<NQ; i++) { 213 ResolveInfo ri = query.get(i); 214 ActivityInfo ai = ri.activityInfo; 215 boolean good = false; 216 217 // ignore SetupWizard package's launcher capability because it is only existed 218 // during SetupWizard is running 219 if (excludeSetupWizardPackage && ai.packageName.equals(mSetupWizardPackageName)) { 220 continue; 221 } 222 223 for (int j=0; j<NS; j++) { 224 if (mSetPackages[j].equals(ai.packageName) 225 && mSetClasses[j].equals(ai.name)) { 226 numMatch++; 227 good = true; 228 break; 229 } 230 } 231 if (!good) return false; 232 } 233 return numMatch == NS; 234 } 235 sameSet(ComponentName[] comps)236 public boolean sameSet(ComponentName[] comps) { 237 if (mSetPackages == null) return false; 238 final int NQ = comps.length; 239 final int NS = mSetPackages.length; 240 int numMatch = 0; 241 for (int i=0; i<NQ; i++) { 242 ComponentName cn = comps[i]; 243 boolean good = false; 244 for (int j=0; j<NS; j++) { 245 if (mSetPackages[j].equals(cn.getPackageName()) 246 && mSetClasses[j].equals(cn.getClassName())) { 247 numMatch++; 248 good = true; 249 break; 250 } 251 } 252 if (!good) return false; 253 } 254 return numMatch == NS; 255 } 256 isSuperset(List<ResolveInfo> query, boolean excludeSetupWizardPackage)257 public boolean isSuperset(List<ResolveInfo> query, boolean excludeSetupWizardPackage) { 258 if (mSetPackages == null) { 259 return query == null; 260 } 261 if (query == null) { 262 return true; 263 } 264 final int NQ = query.size(); 265 final int NS = mSetPackages.length; 266 if (!excludeSetupWizardPackage && NS < NQ) { 267 return false; 268 } 269 for (int i=0; i<NQ; i++) { 270 ResolveInfo ri = query.get(i); 271 ActivityInfo ai = ri.activityInfo; 272 boolean foundMatch = false; 273 274 // ignore SetupWizard package's launcher capability because it is only existed 275 // during SetupWizard is running 276 if (excludeSetupWizardPackage && ai.packageName.equals(mSetupWizardPackageName)) { 277 continue; 278 } 279 280 for (int j=0; j<NS; j++) { 281 if (mSetPackages[j].equals(ai.packageName) && mSetClasses[j].equals(ai.name)) { 282 foundMatch = true; 283 break; 284 } 285 } 286 if (!foundMatch) return false; 287 } 288 return true; 289 } 290 291 /** Returns components from mSetPackages that are present in query. */ discardObsoleteComponents(List<ResolveInfo> query)292 public ComponentName[] discardObsoleteComponents(List<ResolveInfo> query) { 293 if (mSetPackages == null || query == null) { 294 return new ComponentName[0]; 295 } 296 final int NQ = query.size(); 297 final int NS = mSetPackages.length; 298 ArrayList<ComponentName> aliveComponents = new ArrayList<>(); 299 for (int i = 0; i < NQ; i++) { 300 ResolveInfo ri = query.get(i); 301 ActivityInfo ai = ri.activityInfo; 302 for (int j = 0; j < NS; j++) { 303 if (mSetPackages[j].equals(ai.packageName) && mSetClasses[j].equals(ai.name)) { 304 aliveComponents.add(new ComponentName(mSetPackages[j], mSetClasses[j])); 305 break; 306 } 307 } 308 } 309 return aliveComponents.toArray(new ComponentName[aliveComponents.size()]); 310 } 311 dump(PrintWriter out, String prefix, Object ident)312 public void dump(PrintWriter out, String prefix, Object ident) { 313 out.print(prefix); out.print( 314 Integer.toHexString(System.identityHashCode(ident))); 315 out.print(' '); 316 out.println(mShortComponent); 317 out.print(prefix); out.print(" mMatch=0x"); 318 out.print(Integer.toHexString(mMatch)); 319 out.print(" mAlways="); out.println(mAlways); 320 if (mSetComponents != null) { 321 out.print(prefix); out.println(" Selected from:"); 322 for (int i=0; i<mSetComponents.length; i++) { 323 out.print(prefix); out.print(" "); 324 out.println(mSetComponents[i]); 325 } 326 } 327 } 328 } 329