/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.content.ComponentName; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ResolveInfo; import android.util.Slog; import java.io.IOException; import java.io.PrintWriter; import java.util.List; public class PreferredComponent { private static final String TAG_SET = "set"; private static final String ATTR_ALWAYS = "always"; // boolean private static final String ATTR_MATCH = "match"; // number private static final String ATTR_NAME = "name"; // component name private static final String ATTR_SET = "set"; // number public final int mMatch; public final ComponentName mComponent; // Whether this is to be the one that's always chosen. If false, it's the most recently chosen. public boolean mAlways; final String[] mSetPackages; final String[] mSetClasses; final String[] mSetComponents; final String mShortComponent; private String mParseError; private final Callbacks mCallbacks; public interface Callbacks { public boolean onReadTag(String tagName, XmlPullParser parser) throws XmlPullParserException, IOException; } public PreferredComponent(Callbacks callbacks, int match, ComponentName[] set, ComponentName component, boolean always) { mCallbacks = callbacks; mMatch = match&IntentFilter.MATCH_CATEGORY_MASK; mComponent = component; mAlways = always; mShortComponent = component.flattenToShortString(); mParseError = null; if (set != null) { final int N = set.length; String[] myPackages = new String[N]; String[] myClasses = new String[N]; String[] myComponents = new String[N]; for (int i=0; i<N; i++) { ComponentName cn = set[i]; if (cn == null) { mSetPackages = null; mSetClasses = null; mSetComponents = null; return; } myPackages[i] = cn.getPackageName().intern(); myClasses[i] = cn.getClassName().intern(); myComponents[i] = cn.flattenToShortString(); } mSetPackages = myPackages; mSetClasses = myClasses; mSetComponents = myComponents; } else { mSetPackages = null; mSetClasses = null; mSetComponents = null; } } public PreferredComponent(Callbacks callbacks, XmlPullParser parser) throws XmlPullParserException, IOException { mCallbacks = callbacks; mShortComponent = parser.getAttributeValue(null, ATTR_NAME); mComponent = ComponentName.unflattenFromString(mShortComponent); if (mComponent == null) { mParseError = "Bad activity name " + mShortComponent; } String matchStr = parser.getAttributeValue(null, ATTR_MATCH); mMatch = matchStr != null ? Integer.parseInt(matchStr, 16) : 0; String setCountStr = parser.getAttributeValue(null, ATTR_SET); int setCount = setCountStr != null ? Integer.parseInt(setCountStr) : 0; String alwaysStr = parser.getAttributeValue(null, ATTR_ALWAYS); mAlways = alwaysStr != null ? Boolean.parseBoolean(alwaysStr) : true; String[] myPackages = setCount > 0 ? new String[setCount] : null; String[] myClasses = setCount > 0 ? new String[setCount] : null; String[] myComponents = setCount > 0 ? new String[setCount] : null; int setPos = 0; int outerDepth = parser.getDepth(); int type; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } String tagName = parser.getName(); //Log.i(TAG, "Parse outerDepth=" + outerDepth + " depth=" // + parser.getDepth() + " tag=" + tagName); if (tagName.equals(TAG_SET)) { String name = parser.getAttributeValue(null, ATTR_NAME); if (name == null) { if (mParseError == null) { mParseError = "No name in set tag in preferred activity " + mShortComponent; } } else if (setPos >= setCount) { if (mParseError == null) { mParseError = "Too many set tags in preferred activity " + mShortComponent; } } else { ComponentName cn = ComponentName.unflattenFromString(name); if (cn == null) { if (mParseError == null) { mParseError = "Bad set name " + name + " in preferred activity " + mShortComponent; } } else { myPackages[setPos] = cn.getPackageName(); myClasses[setPos] = cn.getClassName(); myComponents[setPos] = name; setPos++; } } XmlUtils.skipCurrentTag(parser); } else if (!mCallbacks.onReadTag(tagName, parser)) { Slog.w("PreferredComponent", "Unknown element: " + parser.getName()); XmlUtils.skipCurrentTag(parser); } } if (setPos != setCount) { if (mParseError == null) { mParseError = "Not enough set tags (expected " + setCount + " but found " + setPos + ") in " + mShortComponent; } } mSetPackages = myPackages; mSetClasses = myClasses; mSetComponents = myComponents; } public String getParseError() { return mParseError; } public void writeToXml(XmlSerializer serializer, boolean full) throws IOException { final int NS = mSetClasses != null ? mSetClasses.length : 0; serializer.attribute(null, ATTR_NAME, mShortComponent); if (full) { if (mMatch != 0) { serializer.attribute(null, ATTR_MATCH, Integer.toHexString(mMatch)); } serializer.attribute(null, ATTR_ALWAYS, Boolean.toString(mAlways)); serializer.attribute(null, ATTR_SET, Integer.toString(NS)); for (int s=0; s<NS; s++) { serializer.startTag(null, TAG_SET); serializer.attribute(null, ATTR_NAME, mSetComponents[s]); serializer.endTag(null, TAG_SET); } } } public boolean sameSet(List<ResolveInfo> query) { if (mSetPackages == null) { return query == null; } if (query == null) { return false; } final int NQ = query.size(); final int NS = mSetPackages.length; int numMatch = 0; for (int i=0; i<NQ; i++) { ResolveInfo ri = query.get(i); ActivityInfo ai = ri.activityInfo; boolean good = false; for (int j=0; j<NS; j++) { if (mSetPackages[j].equals(ai.packageName) && mSetClasses[j].equals(ai.name)) { numMatch++; good = true; break; } } if (!good) return false; } return numMatch == NS; } public boolean sameSet(ComponentName[] comps) { if (mSetPackages == null) return false; final int NQ = comps.length; final int NS = mSetPackages.length; int numMatch = 0; for (int i=0; i<NQ; i++) { ComponentName cn = comps[i]; boolean good = false; for (int j=0; j<NS; j++) { if (mSetPackages[j].equals(cn.getPackageName()) && mSetClasses[j].equals(cn.getClassName())) { numMatch++; good = true; break; } } if (!good) return false; } return numMatch == NS; } public void dump(PrintWriter out, String prefix, Object ident) { out.print(prefix); out.print( Integer.toHexString(System.identityHashCode(ident))); out.print(' '); out.println(mShortComponent); out.print(prefix); out.print(" mMatch=0x"); out.print(Integer.toHexString(mMatch)); out.print(" mAlways="); out.println(mAlways); if (mSetComponents != null) { out.print(prefix); out.println(" Selected from:"); for (int i=0; i<mSetComponents.length; i++) { out.print(prefix); out.print(" "); out.println(mSetComponents[i]); } } } }