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; 18 19 import com.android.internal.util.XmlUtils; 20 21 import org.xmlpull.v1.XmlPullParser; 22 import org.xmlpull.v1.XmlPullParserException; 23 import org.xmlpull.v1.XmlSerializer; 24 25 import android.content.ComponentName; 26 import android.content.IntentFilter; 27 import android.content.pm.ActivityInfo; 28 import android.content.pm.ResolveInfo; 29 import android.util.Slog; 30 31 import java.io.IOException; 32 import java.io.PrintWriter; 33 import java.util.List; 34 35 public class PreferredComponent { 36 private static final String TAG_SET = "set"; 37 private static final String ATTR_ALWAYS = "always"; // boolean 38 private static final String ATTR_MATCH = "match"; // number 39 private static final String ATTR_NAME = "name"; // component name 40 private static final String ATTR_SET = "set"; // number 41 42 public final int mMatch; 43 public final ComponentName mComponent; 44 // Whether this is to be the one that's always chosen. If false, it's the most recently chosen. 45 public boolean mAlways; 46 47 private final String[] mSetPackages; 48 private final String[] mSetClasses; 49 private final String[] mSetComponents; 50 private final String mShortComponent; 51 private String mParseError; 52 53 private final Callbacks mCallbacks; 54 55 public interface Callbacks { 56 public boolean onReadTag(String tagName, XmlPullParser parser) 57 throws XmlPullParserException, IOException; 58 } 59 60 public PreferredComponent(Callbacks callbacks, int match, ComponentName[] set, 61 ComponentName component, boolean always) { 62 mCallbacks = callbacks; 63 mMatch = match&IntentFilter.MATCH_CATEGORY_MASK; 64 mComponent = component; 65 mAlways = always; 66 mShortComponent = component.flattenToShortString(); 67 mParseError = null; 68 if (set != null) { 69 final int N = set.length; 70 String[] myPackages = new String[N]; 71 String[] myClasses = new String[N]; 72 String[] myComponents = new String[N]; 73 for (int i=0; i<N; i++) { 74 ComponentName cn = set[i]; 75 if (cn == null) { 76 mSetPackages = null; 77 mSetClasses = null; 78 mSetComponents = null; 79 return; 80 } 81 myPackages[i] = cn.getPackageName().intern(); 82 myClasses[i] = cn.getClassName().intern(); 83 myComponents[i] = cn.flattenToShortString(); 84 } 85 mSetPackages = myPackages; 86 mSetClasses = myClasses; 87 mSetComponents = myComponents; 88 } else { 89 mSetPackages = null; 90 mSetClasses = null; 91 mSetComponents = null; 92 } 93 } 94 95 public PreferredComponent(Callbacks callbacks, XmlPullParser parser) 96 throws XmlPullParserException, IOException { 97 mCallbacks = callbacks; 98 mShortComponent = parser.getAttributeValue(null, ATTR_NAME); 99 mComponent = ComponentName.unflattenFromString(mShortComponent); 100 if (mComponent == null) { 101 mParseError = "Bad activity name " + mShortComponent; 102 } 103 String matchStr = parser.getAttributeValue(null, ATTR_MATCH); 104 mMatch = matchStr != null ? Integer.parseInt(matchStr, 16) : 0; 105 String setCountStr = parser.getAttributeValue(null, ATTR_SET); 106 int setCount = setCountStr != null ? Integer.parseInt(setCountStr) : 0; 107 String alwaysStr = parser.getAttributeValue(null, ATTR_ALWAYS); 108 mAlways = alwaysStr != null ? Boolean.parseBoolean(alwaysStr) : true; 109 110 String[] myPackages = setCount > 0 ? new String[setCount] : null; 111 String[] myClasses = setCount > 0 ? new String[setCount] : null; 112 String[] myComponents = setCount > 0 ? new String[setCount] : null; 113 114 int setPos = 0; 115 116 int outerDepth = parser.getDepth(); 117 int type; 118 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 119 && (type != XmlPullParser.END_TAG 120 || parser.getDepth() > outerDepth)) { 121 if (type == XmlPullParser.END_TAG 122 || type == XmlPullParser.TEXT) { 123 continue; 124 } 125 126 String tagName = parser.getName(); 127 //Log.i(TAG, "Parse outerDepth=" + outerDepth + " depth=" 128 // + parser.getDepth() + " tag=" + tagName); 129 if (tagName.equals(TAG_SET)) { 130 String name = parser.getAttributeValue(null, ATTR_NAME); 131 if (name == null) { 132 if (mParseError == null) { 133 mParseError = "No name in set tag in preferred activity " 134 + mShortComponent; 135 } 136 } else if (setPos >= setCount) { 137 if (mParseError == null) { 138 mParseError = "Too many set tags in preferred activity " 139 + mShortComponent; 140 } 141 } else { 142 ComponentName cn = ComponentName.unflattenFromString(name); 143 if (cn == null) { 144 if (mParseError == null) { 145 mParseError = "Bad set name " + name + " in preferred activity " 146 + mShortComponent; 147 } 148 } else { 149 myPackages[setPos] = cn.getPackageName(); 150 myClasses[setPos] = cn.getClassName(); 151 myComponents[setPos] = name; 152 setPos++; 153 } 154 } 155 XmlUtils.skipCurrentTag(parser); 156 } else if (!mCallbacks.onReadTag(tagName, parser)) { 157 Slog.w("PreferredComponent", "Unknown element: " + parser.getName()); 158 XmlUtils.skipCurrentTag(parser); 159 } 160 } 161 162 if (setPos != setCount) { 163 if (mParseError == null) { 164 mParseError = "Not enough set tags (expected " + setCount 165 + " but found " + setPos + ") in " + mShortComponent; 166 } 167 } 168 169 mSetPackages = myPackages; 170 mSetClasses = myClasses; 171 mSetComponents = myComponents; 172 } 173 174 public String getParseError() { 175 return mParseError; 176 } 177 178 public void writeToXml(XmlSerializer serializer, boolean full) throws IOException { 179 final int NS = mSetClasses != null ? mSetClasses.length : 0; 180 serializer.attribute(null, ATTR_NAME, mShortComponent); 181 if (full) { 182 if (mMatch != 0) { 183 serializer.attribute(null, ATTR_MATCH, Integer.toHexString(mMatch)); 184 } 185 serializer.attribute(null, ATTR_ALWAYS, Boolean.toString(mAlways)); 186 serializer.attribute(null, ATTR_SET, Integer.toString(NS)); 187 for (int s=0; s<NS; s++) { 188 serializer.startTag(null, TAG_SET); 189 serializer.attribute(null, ATTR_NAME, mSetComponents[s]); 190 serializer.endTag(null, TAG_SET); 191 } 192 } 193 } 194 195 public boolean sameSet(List<ResolveInfo> query, int priority) { 196 if (mSetPackages == null) return false; 197 final int NQ = query.size(); 198 final int NS = mSetPackages.length; 199 int numMatch = 0; 200 for (int i=0; i<NQ; i++) { 201 ResolveInfo ri = query.get(i); 202 if (ri.priority != priority) continue; 203 ActivityInfo ai = ri.activityInfo; 204 boolean good = false; 205 for (int j=0; j<NS; j++) { 206 if (mSetPackages[j].equals(ai.packageName) 207 && mSetClasses[j].equals(ai.name)) { 208 numMatch++; 209 good = true; 210 break; 211 } 212 } 213 if (!good) return false; 214 } 215 return numMatch == NS; 216 } 217 218 public void dump(PrintWriter out, String prefix, Object ident) { 219 out.print(prefix); out.print( 220 Integer.toHexString(System.identityHashCode(ident))); 221 out.print(' '); 222 out.println(mShortComponent); 223 out.print(prefix); out.print(" mMatch=0x"); 224 out.print(Integer.toHexString(mMatch)); 225 out.print(" mAlways="); out.println(mAlways); 226 if (mSetComponents != null) { 227 out.print(prefix); out.println(" Selected from:"); 228 for (int i=0; i<mSetComponents.length; i++) { 229 out.print(prefix); out.print(" "); 230 out.println(mSetComponents[i]); 231 } 232 } 233 } 234 } 235