blob: 8e2e0cde273f47bea34bda0031e99ab96023f1a0 [file] [log] [blame]
/*
* 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]);
}
}
}
}