blob: 3e117d49a3b84a0d31af740a8232b2a67daa9a3f [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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
17package android.content.pm;
18
19import org.xmlpull.v1.XmlPullParser;
20import org.xmlpull.v1.XmlPullParserException;
21
22import android.content.ComponentName;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.res.AssetManager;
26import android.content.res.Configuration;
27import android.content.res.Resources;
28import android.content.res.TypedArray;
29import android.content.res.XmlResourceParser;
30import android.os.Bundle;
31import android.os.PatternMatcher;
32import android.util.AttributeSet;
33import android.util.Config;
34import android.util.DisplayMetrics;
35import android.util.Log;
36import android.util.TypedValue;
37import com.android.internal.util.XmlUtils;
38
39import java.io.File;
40import java.io.IOException;
41import java.io.InputStream;
42import java.lang.ref.WeakReference;
43import java.security.cert.Certificate;
44import java.security.cert.CertificateEncodingException;
45import java.util.ArrayList;
46import java.util.Enumeration;
47import java.util.Iterator;
Mitsuru Oshima8d112672009-04-27 12:01:23 -070048import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import java.util.jar.JarEntry;
50import java.util.jar.JarFile;
51
52/**
53 * Package archive parsing
54 *
55 * {@hide}
56 */
57public class PackageParser {
Dianne Hackborna96cbb42009-05-13 15:06:13 -070058 /** @hide */
59 public static class NewPermissionInfo {
60 public final String name;
61 public final int sdkVersion;
62 public final int fileVersion;
63
64 public NewPermissionInfo(String name, int sdkVersion, int fileVersion) {
65 this.name = name;
66 this.sdkVersion = sdkVersion;
67 this.fileVersion = fileVersion;
68 }
69 }
70
71 /**
72 * List of new permissions that have been added since 1.0.
73 * NOTE: These must be declared in SDK version order, with permissions
74 * added to older SDKs appearing before those added to newer SDKs.
75 * @hide
76 */
Jaikumar Ganesh45515652009-04-23 15:20:21 -070077 public static final PackageParser.NewPermissionInfo NEW_PERMISSIONS[] =
78 new PackageParser.NewPermissionInfo[] {
San Mehat5a3a77d2009-06-01 09:25:28 -070079 new PackageParser.NewPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
Jaikumar Ganesh45515652009-04-23 15:20:21 -070080 android.os.Build.VERSION_CODES.DONUT, 0),
81 new PackageParser.NewPermissionInfo(android.Manifest.permission.READ_PHONE_STATE,
82 android.os.Build.VERSION_CODES.DONUT, 0)
Dianne Hackborna96cbb42009-05-13 15:06:13 -070083 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084
85 private String mArchiveSourcePath;
86 private String[] mSeparateProcesses;
87 private int mSdkVersion;
Dianne Hackborn851a5412009-05-08 12:06:44 -070088 private String mSdkCodename;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089
90 private int mParseError = PackageManager.INSTALL_SUCCEEDED;
91
92 private static final Object mSync = new Object();
93 private static WeakReference<byte[]> mReadBuffer;
94
Mitsuru Oshima69fff4a2009-07-21 09:51:05 -070095 private static boolean sCompatibilityModeEnabled = true;
96
Dianne Hackborn1d442e02009-04-20 18:14:05 -070097 static class ParsePackageItemArgs {
98 final Package owner;
99 final String[] outError;
100 final int nameRes;
101 final int labelRes;
102 final int iconRes;
103
104 String tag;
105 TypedArray sa;
106
107 ParsePackageItemArgs(Package _owner, String[] _outError,
108 int _nameRes, int _labelRes, int _iconRes) {
109 owner = _owner;
110 outError = _outError;
111 nameRes = _nameRes;
112 labelRes = _labelRes;
113 iconRes = _iconRes;
114 }
115 }
116
117 static class ParseComponentArgs extends ParsePackageItemArgs {
118 final String[] sepProcesses;
119 final int processRes;
120 final int enabledRes;
121 int flags;
122
123 ParseComponentArgs(Package _owner, String[] _outError,
124 int _nameRes, int _labelRes, int _iconRes,
125 String[] _sepProcesses, int _processRes,int _enabledRes) {
126 super(_owner, _outError, _nameRes, _labelRes, _iconRes);
127 sepProcesses = _sepProcesses;
128 processRes = _processRes;
129 enabledRes = _enabledRes;
130 }
131 }
132
133 private ParsePackageItemArgs mParseInstrumentationArgs;
134 private ParseComponentArgs mParseActivityArgs;
135 private ParseComponentArgs mParseActivityAliasArgs;
136 private ParseComponentArgs mParseServiceArgs;
137 private ParseComponentArgs mParseProviderArgs;
138
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 /** If set to true, we will only allow package files that exactly match
140 * the DTD. Otherwise, we try to get as much from the package as we
141 * can without failing. This should normally be set to false, to
142 * support extensions to the DTD in future versions. */
143 private static final boolean RIGID_PARSER = false;
144
145 private static final String TAG = "PackageParser";
146
147 public PackageParser(String archiveSourcePath) {
148 mArchiveSourcePath = archiveSourcePath;
149 }
150
151 public void setSeparateProcesses(String[] procs) {
152 mSeparateProcesses = procs;
153 }
154
Dianne Hackborn851a5412009-05-08 12:06:44 -0700155 public void setSdkVersion(int sdkVersion, String codename) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 mSdkVersion = sdkVersion;
Dianne Hackborn851a5412009-05-08 12:06:44 -0700157 mSdkCodename = codename;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158 }
159
160 private static final boolean isPackageFilename(String name) {
161 return name.endsWith(".apk");
162 }
163
164 /**
165 * Generate and return the {@link PackageInfo} for a parsed package.
166 *
167 * @param p the parsed package.
168 * @param flags indicating which optional information is included.
169 */
170 public static PackageInfo generatePackageInfo(PackageParser.Package p,
171 int gids[], int flags) {
172
173 PackageInfo pi = new PackageInfo();
174 pi.packageName = p.packageName;
175 pi.versionCode = p.mVersionCode;
176 pi.versionName = p.mVersionName;
177 pi.sharedUserId = p.mSharedUserId;
178 pi.sharedUserLabel = p.mSharedUserLabel;
179 pi.applicationInfo = p.applicationInfo;
180 if ((flags&PackageManager.GET_GIDS) != 0) {
181 pi.gids = gids;
182 }
183 if ((flags&PackageManager.GET_CONFIGURATIONS) != 0) {
184 int N = p.configPreferences.size();
185 if (N > 0) {
186 pi.configPreferences = new ConfigurationInfo[N];
Dianne Hackborn49237342009-08-27 20:08:01 -0700187 p.configPreferences.toArray(pi.configPreferences);
188 }
189 N = p.reqFeatures != null ? p.reqFeatures.size() : 0;
190 if (N > 0) {
191 pi.reqFeatures = new FeatureInfo[N];
192 p.reqFeatures.toArray(pi.reqFeatures);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 }
194 }
195 if ((flags&PackageManager.GET_ACTIVITIES) != 0) {
196 int N = p.activities.size();
197 if (N > 0) {
Dianne Hackborn7eca6872009-09-28 23:57:05 -0700198 if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
199 pi.activities = new ActivityInfo[N];
200 } else {
201 int num = 0;
202 for (int i=0; i<N; i++) {
203 if (p.activities.get(i).info.enabled) num++;
204 }
205 pi.activities = new ActivityInfo[num];
206 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 for (int i=0; i<N; i++) {
208 final Activity activity = p.activities.get(i);
209 if (activity.info.enabled
210 || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
211 pi.activities[i] = generateActivityInfo(p.activities.get(i), flags);
212 }
213 }
214 }
215 }
216 if ((flags&PackageManager.GET_RECEIVERS) != 0) {
217 int N = p.receivers.size();
218 if (N > 0) {
Dianne Hackborn7eca6872009-09-28 23:57:05 -0700219 if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
220 pi.receivers = new ActivityInfo[N];
221 } else {
222 int num = 0;
223 for (int i=0; i<N; i++) {
224 if (p.receivers.get(i).info.enabled) num++;
225 }
226 pi.receivers = new ActivityInfo[num];
227 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 for (int i=0; i<N; i++) {
229 final Activity activity = p.receivers.get(i);
230 if (activity.info.enabled
231 || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
232 pi.receivers[i] = generateActivityInfo(p.receivers.get(i), flags);
233 }
234 }
235 }
236 }
237 if ((flags&PackageManager.GET_SERVICES) != 0) {
238 int N = p.services.size();
239 if (N > 0) {
Dianne Hackborn7eca6872009-09-28 23:57:05 -0700240 if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
241 pi.services = new ServiceInfo[N];
242 } else {
243 int num = 0;
244 for (int i=0; i<N; i++) {
245 if (p.services.get(i).info.enabled) num++;
246 }
247 pi.services = new ServiceInfo[num];
248 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 for (int i=0; i<N; i++) {
250 final Service service = p.services.get(i);
251 if (service.info.enabled
252 || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
253 pi.services[i] = generateServiceInfo(p.services.get(i), flags);
254 }
255 }
256 }
257 }
258 if ((flags&PackageManager.GET_PROVIDERS) != 0) {
259 int N = p.providers.size();
260 if (N > 0) {
Dianne Hackborn7eca6872009-09-28 23:57:05 -0700261 if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
262 pi.providers = new ProviderInfo[N];
263 } else {
264 int num = 0;
265 for (int i=0; i<N; i++) {
266 if (p.providers.get(i).info.enabled) num++;
267 }
268 pi.providers = new ProviderInfo[num];
269 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 for (int i=0; i<N; i++) {
271 final Provider provider = p.providers.get(i);
272 if (provider.info.enabled
273 || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
274 pi.providers[i] = generateProviderInfo(p.providers.get(i), flags);
275 }
276 }
277 }
278 }
279 if ((flags&PackageManager.GET_INSTRUMENTATION) != 0) {
280 int N = p.instrumentation.size();
281 if (N > 0) {
282 pi.instrumentation = new InstrumentationInfo[N];
283 for (int i=0; i<N; i++) {
284 pi.instrumentation[i] = generateInstrumentationInfo(
285 p.instrumentation.get(i), flags);
286 }
287 }
288 }
289 if ((flags&PackageManager.GET_PERMISSIONS) != 0) {
290 int N = p.permissions.size();
291 if (N > 0) {
292 pi.permissions = new PermissionInfo[N];
293 for (int i=0; i<N; i++) {
294 pi.permissions[i] = generatePermissionInfo(p.permissions.get(i), flags);
295 }
296 }
297 N = p.requestedPermissions.size();
298 if (N > 0) {
299 pi.requestedPermissions = new String[N];
300 for (int i=0; i<N; i++) {
301 pi.requestedPermissions[i] = p.requestedPermissions.get(i);
302 }
303 }
304 }
305 if ((flags&PackageManager.GET_SIGNATURES) != 0) {
Suchi Amalapurapuc7485412009-08-27 12:28:09 -0700306 if (p.mSignatures != null) {
307 int N = p.mSignatures.length;
308 if (N > 0) {
309 pi.signatures = new Signature[N];
310 System.arraycopy(p.mSignatures, 0, pi.signatures, 0, N);
311 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 }
313 }
314 return pi;
315 }
316
317 private Certificate[] loadCertificates(JarFile jarFile, JarEntry je,
318 byte[] readBuffer) {
319 try {
320 // We must read the stream for the JarEntry to retrieve
321 // its certificates.
322 InputStream is = jarFile.getInputStream(je);
323 while (is.read(readBuffer, 0, readBuffer.length) != -1) {
324 // not using
325 }
326 is.close();
327 return je != null ? je.getCertificates() : null;
328 } catch (IOException e) {
329 Log.w(TAG, "Exception reading " + je.getName() + " in "
330 + jarFile.getName(), e);
331 }
332 return null;
333 }
334
335 public final static int PARSE_IS_SYSTEM = 0x0001;
336 public final static int PARSE_CHATTY = 0x0002;
337 public final static int PARSE_MUST_BE_APK = 0x0004;
338 public final static int PARSE_IGNORE_PROCESSES = 0x0008;
339
340 public int getParseError() {
341 return mParseError;
342 }
343
344 public Package parsePackage(File sourceFile, String destFileName,
345 DisplayMetrics metrics, int flags) {
346 mParseError = PackageManager.INSTALL_SUCCEEDED;
347
348 mArchiveSourcePath = sourceFile.getPath();
349 if (!sourceFile.isFile()) {
350 Log.w(TAG, "Skipping dir: " + mArchiveSourcePath);
351 mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
352 return null;
353 }
354 if (!isPackageFilename(sourceFile.getName())
355 && (flags&PARSE_MUST_BE_APK) != 0) {
356 if ((flags&PARSE_IS_SYSTEM) == 0) {
357 // We expect to have non-.apk files in the system dir,
358 // so don't warn about them.
359 Log.w(TAG, "Skipping non-package file: " + mArchiveSourcePath);
360 }
361 mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
362 return null;
363 }
364
365 if ((flags&PARSE_CHATTY) != 0 && Config.LOGD) Log.d(
366 TAG, "Scanning package: " + mArchiveSourcePath);
367
368 XmlResourceParser parser = null;
369 AssetManager assmgr = null;
370 boolean assetError = true;
371 try {
372 assmgr = new AssetManager();
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700373 int cookie = assmgr.addAssetPath(mArchiveSourcePath);
374 if(cookie != 0) {
375 parser = assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 assetError = false;
377 } else {
378 Log.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);
379 }
380 } catch (Exception e) {
381 Log.w(TAG, "Unable to read AndroidManifest.xml of "
382 + mArchiveSourcePath, e);
383 }
384 if(assetError) {
385 if (assmgr != null) assmgr.close();
386 mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
387 return null;
388 }
389 String[] errorText = new String[1];
390 Package pkg = null;
391 Exception errorException = null;
392 try {
393 // XXXX todo: need to figure out correct configuration.
394 Resources res = new Resources(assmgr, metrics, null);
395 pkg = parsePackage(res, parser, flags, errorText);
396 } catch (Exception e) {
397 errorException = e;
398 mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
399 }
400
401
402 if (pkg == null) {
403 if (errorException != null) {
404 Log.w(TAG, mArchiveSourcePath, errorException);
405 } else {
406 Log.w(TAG, mArchiveSourcePath + " (at "
407 + parser.getPositionDescription()
408 + "): " + errorText[0]);
409 }
410 parser.close();
411 assmgr.close();
412 if (mParseError == PackageManager.INSTALL_SUCCEEDED) {
413 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
414 }
415 return null;
416 }
417
418 parser.close();
419 assmgr.close();
420
421 pkg.applicationInfo.sourceDir = destFileName;
422 pkg.applicationInfo.publicSourceDir = destFileName;
423 pkg.mSignatures = null;
424
425 return pkg;
426 }
427
428 public boolean collectCertificates(Package pkg, int flags) {
429 pkg.mSignatures = null;
430
431 WeakReference<byte[]> readBufferRef;
432 byte[] readBuffer = null;
433 synchronized (mSync) {
434 readBufferRef = mReadBuffer;
435 if (readBufferRef != null) {
436 mReadBuffer = null;
437 readBuffer = readBufferRef.get();
438 }
439 if (readBuffer == null) {
440 readBuffer = new byte[8192];
441 readBufferRef = new WeakReference<byte[]>(readBuffer);
442 }
443 }
444
445 try {
446 JarFile jarFile = new JarFile(mArchiveSourcePath);
447
448 Certificate[] certs = null;
449
450 if ((flags&PARSE_IS_SYSTEM) != 0) {
451 // If this package comes from the system image, then we
452 // can trust it... we'll just use the AndroidManifest.xml
453 // to retrieve its signatures, not validating all of the
454 // files.
455 JarEntry jarEntry = jarFile.getJarEntry("AndroidManifest.xml");
456 certs = loadCertificates(jarFile, jarEntry, readBuffer);
457 if (certs == null) {
458 Log.e(TAG, "Package " + pkg.packageName
459 + " has no certificates at entry "
460 + jarEntry.getName() + "; ignoring!");
461 jarFile.close();
462 mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
463 return false;
464 }
465 if (false) {
466 Log.i(TAG, "File " + mArchiveSourcePath + ": entry=" + jarEntry
467 + " certs=" + (certs != null ? certs.length : 0));
468 if (certs != null) {
469 final int N = certs.length;
470 for (int i=0; i<N; i++) {
471 Log.i(TAG, " Public key: "
472 + certs[i].getPublicKey().getEncoded()
473 + " " + certs[i].getPublicKey());
474 }
475 }
476 }
477
478 } else {
479 Enumeration entries = jarFile.entries();
480 while (entries.hasMoreElements()) {
481 JarEntry je = (JarEntry)entries.nextElement();
482 if (je.isDirectory()) continue;
483 if (je.getName().startsWith("META-INF/")) continue;
484 Certificate[] localCerts = loadCertificates(jarFile, je,
485 readBuffer);
486 if (false) {
487 Log.i(TAG, "File " + mArchiveSourcePath + " entry " + je.getName()
488 + ": certs=" + certs + " ("
489 + (certs != null ? certs.length : 0) + ")");
490 }
491 if (localCerts == null) {
492 Log.e(TAG, "Package " + pkg.packageName
493 + " has no certificates at entry "
494 + je.getName() + "; ignoring!");
495 jarFile.close();
496 mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
497 return false;
498 } else if (certs == null) {
499 certs = localCerts;
500 } else {
501 // Ensure all certificates match.
502 for (int i=0; i<certs.length; i++) {
503 boolean found = false;
504 for (int j=0; j<localCerts.length; j++) {
505 if (certs[i] != null &&
506 certs[i].equals(localCerts[j])) {
507 found = true;
508 break;
509 }
510 }
511 if (!found || certs.length != localCerts.length) {
512 Log.e(TAG, "Package " + pkg.packageName
513 + " has mismatched certificates at entry "
514 + je.getName() + "; ignoring!");
515 jarFile.close();
516 mParseError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
517 return false;
518 }
519 }
520 }
521 }
522 }
523 jarFile.close();
524
525 synchronized (mSync) {
526 mReadBuffer = readBufferRef;
527 }
528
529 if (certs != null && certs.length > 0) {
530 final int N = certs.length;
531 pkg.mSignatures = new Signature[certs.length];
532 for (int i=0; i<N; i++) {
533 pkg.mSignatures[i] = new Signature(
534 certs[i].getEncoded());
535 }
536 } else {
537 Log.e(TAG, "Package " + pkg.packageName
538 + " has no certificates; ignoring!");
539 mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
540 return false;
541 }
542 } catch (CertificateEncodingException e) {
543 Log.w(TAG, "Exception reading " + mArchiveSourcePath, e);
544 mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
545 return false;
546 } catch (IOException e) {
547 Log.w(TAG, "Exception reading " + mArchiveSourcePath, e);
548 mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
549 return false;
550 } catch (RuntimeException e) {
551 Log.w(TAG, "Exception reading " + mArchiveSourcePath, e);
552 mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
553 return false;
554 }
555
556 return true;
557 }
558
559 public static String parsePackageName(String packageFilePath, int flags) {
560 XmlResourceParser parser = null;
561 AssetManager assmgr = null;
562 try {
563 assmgr = new AssetManager();
564 int cookie = assmgr.addAssetPath(packageFilePath);
565 parser = assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml");
566 } catch (Exception e) {
567 if (assmgr != null) assmgr.close();
568 Log.w(TAG, "Unable to read AndroidManifest.xml of "
569 + packageFilePath, e);
570 return null;
571 }
572 AttributeSet attrs = parser;
573 String errors[] = new String[1];
574 String packageName = null;
575 try {
576 packageName = parsePackageName(parser, attrs, flags, errors);
577 } catch (IOException e) {
578 Log.w(TAG, packageFilePath, e);
579 } catch (XmlPullParserException e) {
580 Log.w(TAG, packageFilePath, e);
581 } finally {
582 if (parser != null) parser.close();
583 if (assmgr != null) assmgr.close();
584 }
585 if (packageName == null) {
586 Log.e(TAG, "parsePackageName error: " + errors[0]);
587 return null;
588 }
589 return packageName;
590 }
591
592 private static String validateName(String name, boolean requiresSeparator) {
593 final int N = name.length();
594 boolean hasSep = false;
595 boolean front = true;
596 for (int i=0; i<N; i++) {
597 final char c = name.charAt(i);
598 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
599 front = false;
600 continue;
601 }
602 if (!front) {
603 if ((c >= '0' && c <= '9') || c == '_') {
604 continue;
605 }
606 }
607 if (c == '.') {
608 hasSep = true;
609 front = true;
610 continue;
611 }
612 return "bad character '" + c + "'";
613 }
614 return hasSep || !requiresSeparator
615 ? null : "must have at least one '.' separator";
616 }
617
618 private static String parsePackageName(XmlPullParser parser,
619 AttributeSet attrs, int flags, String[] outError)
620 throws IOException, XmlPullParserException {
621
622 int type;
623 while ((type=parser.next()) != parser.START_TAG
624 && type != parser.END_DOCUMENT) {
625 ;
626 }
627
628 if (type != parser.START_TAG) {
629 outError[0] = "No start tag found";
630 return null;
631 }
632 if ((flags&PARSE_CHATTY) != 0 && Config.LOGV) Log.v(
633 TAG, "Root element name: '" + parser.getName() + "'");
634 if (!parser.getName().equals("manifest")) {
635 outError[0] = "No <manifest> tag";
636 return null;
637 }
638 String pkgName = attrs.getAttributeValue(null, "package");
639 if (pkgName == null || pkgName.length() == 0) {
640 outError[0] = "<manifest> does not specify package";
641 return null;
642 }
643 String nameError = validateName(pkgName, true);
644 if (nameError != null && !"android".equals(pkgName)) {
645 outError[0] = "<manifest> specifies bad package name \""
646 + pkgName + "\": " + nameError;
647 return null;
648 }
649
650 return pkgName.intern();
651 }
652
653 /**
654 * Temporary.
655 */
656 static public Signature stringToSignature(String str) {
657 final int N = str.length();
658 byte[] sig = new byte[N];
659 for (int i=0; i<N; i++) {
660 sig[i] = (byte)str.charAt(i);
661 }
662 return new Signature(sig);
663 }
664
665 private Package parsePackage(
666 Resources res, XmlResourceParser parser, int flags, String[] outError)
667 throws XmlPullParserException, IOException {
668 AttributeSet attrs = parser;
669
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700670 mParseInstrumentationArgs = null;
671 mParseActivityArgs = null;
672 mParseServiceArgs = null;
673 mParseProviderArgs = null;
674
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675 String pkgName = parsePackageName(parser, attrs, flags, outError);
676 if (pkgName == null) {
677 mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
678 return null;
679 }
680 int type;
681
682 final Package pkg = new Package(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683 boolean foundApp = false;
Dianne Hackborn851a5412009-05-08 12:06:44 -0700684
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800685 TypedArray sa = res.obtainAttributes(attrs,
686 com.android.internal.R.styleable.AndroidManifest);
687 pkg.mVersionCode = sa.getInteger(
688 com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
689 pkg.mVersionName = sa.getNonResourceString(
690 com.android.internal.R.styleable.AndroidManifest_versionName);
691 if (pkg.mVersionName != null) {
692 pkg.mVersionName = pkg.mVersionName.intern();
693 }
694 String str = sa.getNonResourceString(
695 com.android.internal.R.styleable.AndroidManifest_sharedUserId);
696 if (str != null) {
697 String nameError = validateName(str, true);
698 if (nameError != null && !"android".equals(pkgName)) {
699 outError[0] = "<manifest> specifies bad sharedUserId name \""
700 + str + "\": " + nameError;
701 mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
702 return null;
703 }
704 pkg.mSharedUserId = str.intern();
705 pkg.mSharedUserLabel = sa.getResourceId(
706 com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
707 }
708 sa.recycle();
709
Dianne Hackborn723738c2009-06-25 19:48:04 -0700710 // Resource boolean are -1, so 1 means we don't know the value.
711 int supportsSmallScreens = 1;
712 int supportsNormalScreens = 1;
713 int supportsLargeScreens = 1;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700714 int resizeable = 1;
Dianne Hackborn11b822d2009-07-21 20:03:02 -0700715 int anyDensity = 1;
Dianne Hackborn723738c2009-06-25 19:48:04 -0700716
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800717 int outerDepth = parser.getDepth();
718 while ((type=parser.next()) != parser.END_DOCUMENT
719 && (type != parser.END_TAG || parser.getDepth() > outerDepth)) {
720 if (type == parser.END_TAG || type == parser.TEXT) {
721 continue;
722 }
723
724 String tagName = parser.getName();
725 if (tagName.equals("application")) {
726 if (foundApp) {
727 if (RIGID_PARSER) {
728 outError[0] = "<manifest> has more than one <application>";
729 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
730 return null;
731 } else {
732 Log.w(TAG, "<manifest> has more than one <application>");
733 XmlUtils.skipCurrentTag(parser);
734 continue;
735 }
736 }
737
738 foundApp = true;
739 if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {
740 return null;
741 }
742 } else if (tagName.equals("permission-group")) {
743 if (parsePermissionGroup(pkg, res, parser, attrs, outError) == null) {
744 return null;
745 }
746 } else if (tagName.equals("permission")) {
747 if (parsePermission(pkg, res, parser, attrs, outError) == null) {
748 return null;
749 }
750 } else if (tagName.equals("permission-tree")) {
751 if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
752 return null;
753 }
754 } else if (tagName.equals("uses-permission")) {
755 sa = res.obtainAttributes(attrs,
756 com.android.internal.R.styleable.AndroidManifestUsesPermission);
757
758 String name = sa.getNonResourceString(
759 com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
760
761 sa.recycle();
762
763 if (name != null && !pkg.requestedPermissions.contains(name)) {
Dianne Hackborn854060a2009-07-09 18:14:31 -0700764 pkg.requestedPermissions.add(name.intern());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800765 }
766
767 XmlUtils.skipCurrentTag(parser);
768
769 } else if (tagName.equals("uses-configuration")) {
770 ConfigurationInfo cPref = new ConfigurationInfo();
771 sa = res.obtainAttributes(attrs,
772 com.android.internal.R.styleable.AndroidManifestUsesConfiguration);
773 cPref.reqTouchScreen = sa.getInt(
774 com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
775 Configuration.TOUCHSCREEN_UNDEFINED);
776 cPref.reqKeyboardType = sa.getInt(
777 com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
778 Configuration.KEYBOARD_UNDEFINED);
779 if (sa.getBoolean(
780 com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
781 false)) {
782 cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
783 }
784 cPref.reqNavigation = sa.getInt(
785 com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
786 Configuration.NAVIGATION_UNDEFINED);
787 if (sa.getBoolean(
788 com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
789 false)) {
790 cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
791 }
792 sa.recycle();
793 pkg.configPreferences.add(cPref);
794
795 XmlUtils.skipCurrentTag(parser);
796
Suchi Amalapurapud299b812009-06-05 10:26:19 -0700797 } else if (tagName.equals("uses-feature")) {
Dianne Hackborn49237342009-08-27 20:08:01 -0700798 FeatureInfo fi = new FeatureInfo();
Suchi Amalapurapud299b812009-06-05 10:26:19 -0700799 sa = res.obtainAttributes(attrs,
800 com.android.internal.R.styleable.AndroidManifestUsesFeature);
Dianne Hackborn49237342009-08-27 20:08:01 -0700801 fi.name = sa.getNonResourceString(
802 com.android.internal.R.styleable.AndroidManifestUsesFeature_name);
803 if (fi.name == null) {
804 fi.reqGlEsVersion = sa.getInt(
805 com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion,
806 FeatureInfo.GL_ES_VERSION_UNDEFINED);
807 }
808 if (sa.getBoolean(
809 com.android.internal.R.styleable.AndroidManifestUsesFeature_required,
810 true)) {
811 fi.flags |= FeatureInfo.FLAG_REQUIRED;
812 }
Suchi Amalapurapud299b812009-06-05 10:26:19 -0700813 sa.recycle();
Dianne Hackborn49237342009-08-27 20:08:01 -0700814 if (pkg.reqFeatures == null) {
815 pkg.reqFeatures = new ArrayList<FeatureInfo>();
816 }
817 pkg.reqFeatures.add(fi);
818
819 if (fi.name == null) {
820 ConfigurationInfo cPref = new ConfigurationInfo();
821 cPref.reqGlEsVersion = fi.reqGlEsVersion;
822 pkg.configPreferences.add(cPref);
823 }
Suchi Amalapurapud299b812009-06-05 10:26:19 -0700824
825 XmlUtils.skipCurrentTag(parser);
826
Dianne Hackborn851a5412009-05-08 12:06:44 -0700827 } else if (tagName.equals("uses-sdk")) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 if (mSdkVersion > 0) {
829 sa = res.obtainAttributes(attrs,
830 com.android.internal.R.styleable.AndroidManifestUsesSdk);
831
Dianne Hackborn851a5412009-05-08 12:06:44 -0700832 int minVers = 0;
833 String minCode = null;
834 int targetVers = 0;
835 String targetCode = null;
836
837 TypedValue val = sa.peekValue(
838 com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
839 if (val != null) {
840 if (val.type == TypedValue.TYPE_STRING && val.string != null) {
841 targetCode = minCode = val.string.toString();
842 } else {
843 // If it's not a string, it's an integer.
Dianne Hackborn5c1e00b2009-06-18 17:10:57 -0700844 targetVers = minVers = val.data;
Dianne Hackborn851a5412009-05-08 12:06:44 -0700845 }
846 }
847
848 val = sa.peekValue(
849 com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
850 if (val != null) {
851 if (val.type == TypedValue.TYPE_STRING && val.string != null) {
852 targetCode = minCode = val.string.toString();
853 } else {
854 // If it's not a string, it's an integer.
855 targetVers = val.data;
856 }
857 }
858
859 int maxVers = sa.getInt(
860 com.android.internal.R.styleable.AndroidManifestUsesSdk_maxSdkVersion,
861 mSdkVersion);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862
863 sa.recycle();
864
Dianne Hackborn5c1e00b2009-06-18 17:10:57 -0700865 if (minCode != null) {
866 if (!minCode.equals(mSdkCodename)) {
867 if (mSdkCodename != null) {
868 outError[0] = "Requires development platform " + minCode
869 + " (current platform is " + mSdkCodename + ")";
870 } else {
871 outError[0] = "Requires development platform " + minCode
872 + " but this is a release platform.";
873 }
874 mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
875 return null;
876 }
877 } else if (minVers > mSdkVersion) {
878 outError[0] = "Requires newer sdk version #" + minVers
879 + " (current version is #" + mSdkVersion + ")";
880 mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
881 return null;
882 }
883
Dianne Hackborn851a5412009-05-08 12:06:44 -0700884 if (targetCode != null) {
885 if (!targetCode.equals(mSdkCodename)) {
886 if (mSdkCodename != null) {
887 outError[0] = "Requires development platform " + targetCode
888 + " (current platform is " + mSdkCodename + ")";
889 } else {
890 outError[0] = "Requires development platform " + targetCode
891 + " but this is a release platform.";
892 }
893 mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
894 return null;
895 }
896 // If the code matches, it definitely targets this SDK.
Dianne Hackborna96cbb42009-05-13 15:06:13 -0700897 pkg.applicationInfo.targetSdkVersion
898 = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
899 } else {
900 pkg.applicationInfo.targetSdkVersion = targetVers;
Dianne Hackborn851a5412009-05-08 12:06:44 -0700901 }
902
Dianne Hackborn851a5412009-05-08 12:06:44 -0700903 if (maxVers < mSdkVersion) {
904 outError[0] = "Requires older sdk version #" + maxVers
905 + " (current version is #" + mSdkVersion + ")";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906 mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
907 return null;
908 }
909 }
910
911 XmlUtils.skipCurrentTag(parser);
912
Dianne Hackborn723738c2009-06-25 19:48:04 -0700913 } else if (tagName.equals("supports-screens")) {
914 sa = res.obtainAttributes(attrs,
915 com.android.internal.R.styleable.AndroidManifestSupportsScreens);
916
917 // This is a trick to get a boolean and still able to detect
918 // if a value was actually set.
919 supportsSmallScreens = sa.getInteger(
920 com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens,
921 supportsSmallScreens);
922 supportsNormalScreens = sa.getInteger(
923 com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens,
924 supportsNormalScreens);
925 supportsLargeScreens = sa.getInteger(
926 com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens,
927 supportsLargeScreens);
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700928 resizeable = sa.getInteger(
929 com.android.internal.R.styleable.AndroidManifestSupportsScreens_resizeable,
930 supportsLargeScreens);
Dianne Hackborn11b822d2009-07-21 20:03:02 -0700931 anyDensity = sa.getInteger(
932 com.android.internal.R.styleable.AndroidManifestSupportsScreens_anyDensity,
933 anyDensity);
Dianne Hackborn723738c2009-06-25 19:48:04 -0700934
935 sa.recycle();
936
Mitsuru Oshima9189cab2009-06-03 11:19:12 -0700937 XmlUtils.skipCurrentTag(parser);
Dianne Hackborn854060a2009-07-09 18:14:31 -0700938
939 } else if (tagName.equals("protected-broadcast")) {
940 sa = res.obtainAttributes(attrs,
941 com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);
942
943 String name = sa.getNonResourceString(
944 com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);
945
946 sa.recycle();
947
948 if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {
949 if (pkg.protectedBroadcasts == null) {
950 pkg.protectedBroadcasts = new ArrayList<String>();
951 }
952 if (!pkg.protectedBroadcasts.contains(name)) {
953 pkg.protectedBroadcasts.add(name.intern());
954 }
955 }
956
957 XmlUtils.skipCurrentTag(parser);
958
959 } else if (tagName.equals("instrumentation")) {
960 if (parseInstrumentation(pkg, res, parser, attrs, outError) == null) {
961 return null;
962 }
963
964 } else if (tagName.equals("eat-comment")) {
965 // Just skip this tag
966 XmlUtils.skipCurrentTag(parser);
967 continue;
968
969 } else if (RIGID_PARSER) {
970 outError[0] = "Bad element under <manifest>: "
971 + parser.getName();
972 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
973 return null;
974
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800975 } else {
976 Log.w(TAG, "Bad element under <manifest>: "
977 + parser.getName());
978 XmlUtils.skipCurrentTag(parser);
979 continue;
980 }
981 }
982
983 if (!foundApp && pkg.instrumentation.size() == 0) {
984 outError[0] = "<manifest> does not contain an <application> or <instrumentation>";
985 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY;
986 }
987
Dianne Hackborna96cbb42009-05-13 15:06:13 -0700988 final int NP = PackageParser.NEW_PERMISSIONS.length;
989 for (int ip=0; ip<NP; ip++) {
990 final PackageParser.NewPermissionInfo npi
991 = PackageParser.NEW_PERMISSIONS[ip];
992 if (pkg.applicationInfo.targetSdkVersion >= npi.sdkVersion) {
993 break;
994 }
995 if (!pkg.requestedPermissions.contains(npi.name)) {
996 Log.i(TAG, "Impliciting adding " + npi.name + " to old pkg "
997 + pkg.packageName);
998 pkg.requestedPermissions.add(npi.name);
999 }
Dianne Hackborn851a5412009-05-08 12:06:44 -07001000 }
1001
Dianne Hackborn723738c2009-06-25 19:48:04 -07001002 if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
1003 && pkg.applicationInfo.targetSdkVersion
Dianne Hackborn11b822d2009-07-21 20:03:02 -07001004 >= android.os.Build.VERSION_CODES.DONUT)) {
Dianne Hackborn723738c2009-06-25 19:48:04 -07001005 pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
1006 }
1007 if (supportsNormalScreens != 0) {
1008 pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
1009 }
1010 if (supportsLargeScreens < 0 || (supportsLargeScreens > 0
1011 && pkg.applicationInfo.targetSdkVersion
Dianne Hackborn11b822d2009-07-21 20:03:02 -07001012 >= android.os.Build.VERSION_CODES.DONUT)) {
Dianne Hackborn723738c2009-06-25 19:48:04 -07001013 pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
1014 }
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001015 if (resizeable < 0 || (resizeable > 0
1016 && pkg.applicationInfo.targetSdkVersion
Dianne Hackborn11b822d2009-07-21 20:03:02 -07001017 >= android.os.Build.VERSION_CODES.DONUT)) {
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001018 pkg.applicationInfo.flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
1019 }
Dianne Hackborn11b822d2009-07-21 20:03:02 -07001020 if (anyDensity < 0 || (anyDensity > 0
1021 && pkg.applicationInfo.targetSdkVersion
1022 >= android.os.Build.VERSION_CODES.DONUT)) {
1023 pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
Mitsuru Oshima8d112672009-04-27 12:01:23 -07001024 }
Mitsuru Oshima1ecf5d22009-07-06 17:20:38 -07001025
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 return pkg;
1027 }
1028
1029 private static String buildClassName(String pkg, CharSequence clsSeq,
1030 String[] outError) {
1031 if (clsSeq == null || clsSeq.length() <= 0) {
1032 outError[0] = "Empty class name in package " + pkg;
1033 return null;
1034 }
1035 String cls = clsSeq.toString();
1036 char c = cls.charAt(0);
1037 if (c == '.') {
1038 return (pkg + cls).intern();
1039 }
1040 if (cls.indexOf('.') < 0) {
1041 StringBuilder b = new StringBuilder(pkg);
1042 b.append('.');
1043 b.append(cls);
1044 return b.toString().intern();
1045 }
1046 if (c >= 'a' && c <= 'z') {
1047 return cls.intern();
1048 }
1049 outError[0] = "Bad class name " + cls + " in package " + pkg;
1050 return null;
1051 }
1052
1053 private static String buildCompoundName(String pkg,
1054 CharSequence procSeq, String type, String[] outError) {
1055 String proc = procSeq.toString();
1056 char c = proc.charAt(0);
1057 if (pkg != null && c == ':') {
1058 if (proc.length() < 2) {
1059 outError[0] = "Bad " + type + " name " + proc + " in package " + pkg
1060 + ": must be at least two characters";
1061 return null;
1062 }
1063 String subName = proc.substring(1);
1064 String nameError = validateName(subName, false);
1065 if (nameError != null) {
1066 outError[0] = "Invalid " + type + " name " + proc + " in package "
1067 + pkg + ": " + nameError;
1068 return null;
1069 }
1070 return (pkg + proc).intern();
1071 }
1072 String nameError = validateName(proc, true);
1073 if (nameError != null && !"system".equals(proc)) {
1074 outError[0] = "Invalid " + type + " name " + proc + " in package "
1075 + pkg + ": " + nameError;
1076 return null;
1077 }
1078 return proc.intern();
1079 }
1080
1081 private static String buildProcessName(String pkg, String defProc,
1082 CharSequence procSeq, int flags, String[] separateProcesses,
1083 String[] outError) {
1084 if ((flags&PARSE_IGNORE_PROCESSES) != 0 && !"system".equals(procSeq)) {
1085 return defProc != null ? defProc : pkg;
1086 }
1087 if (separateProcesses != null) {
1088 for (int i=separateProcesses.length-1; i>=0; i--) {
1089 String sp = separateProcesses[i];
1090 if (sp.equals(pkg) || sp.equals(defProc) || sp.equals(procSeq)) {
1091 return pkg;
1092 }
1093 }
1094 }
1095 if (procSeq == null || procSeq.length() <= 0) {
1096 return defProc;
1097 }
1098 return buildCompoundName(pkg, procSeq, "package", outError);
1099 }
1100
1101 private static String buildTaskAffinityName(String pkg, String defProc,
1102 CharSequence procSeq, String[] outError) {
1103 if (procSeq == null) {
1104 return defProc;
1105 }
1106 if (procSeq.length() <= 0) {
1107 return null;
1108 }
1109 return buildCompoundName(pkg, procSeq, "taskAffinity", outError);
1110 }
1111
1112 private PermissionGroup parsePermissionGroup(Package owner, Resources res,
1113 XmlPullParser parser, AttributeSet attrs, String[] outError)
1114 throws XmlPullParserException, IOException {
1115 PermissionGroup perm = new PermissionGroup(owner);
1116
1117 TypedArray sa = res.obtainAttributes(attrs,
1118 com.android.internal.R.styleable.AndroidManifestPermissionGroup);
1119
1120 if (!parsePackageItemInfo(owner, perm.info, outError,
1121 "<permission-group>", sa,
1122 com.android.internal.R.styleable.AndroidManifestPermissionGroup_name,
1123 com.android.internal.R.styleable.AndroidManifestPermissionGroup_label,
1124 com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon)) {
1125 sa.recycle();
1126 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1127 return null;
1128 }
1129
1130 perm.info.descriptionRes = sa.getResourceId(
1131 com.android.internal.R.styleable.AndroidManifestPermissionGroup_description,
1132 0);
1133
1134 sa.recycle();
1135
1136 if (!parseAllMetaData(res, parser, attrs, "<permission-group>", perm,
1137 outError)) {
1138 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1139 return null;
1140 }
1141
1142 owner.permissionGroups.add(perm);
1143
1144 return perm;
1145 }
1146
1147 private Permission parsePermission(Package owner, Resources res,
1148 XmlPullParser parser, AttributeSet attrs, String[] outError)
1149 throws XmlPullParserException, IOException {
1150 Permission perm = new Permission(owner);
1151
1152 TypedArray sa = res.obtainAttributes(attrs,
1153 com.android.internal.R.styleable.AndroidManifestPermission);
1154
1155 if (!parsePackageItemInfo(owner, perm.info, outError,
1156 "<permission>", sa,
1157 com.android.internal.R.styleable.AndroidManifestPermission_name,
1158 com.android.internal.R.styleable.AndroidManifestPermission_label,
1159 com.android.internal.R.styleable.AndroidManifestPermission_icon)) {
1160 sa.recycle();
1161 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1162 return null;
1163 }
1164
1165 perm.info.group = sa.getNonResourceString(
1166 com.android.internal.R.styleable.AndroidManifestPermission_permissionGroup);
1167 if (perm.info.group != null) {
1168 perm.info.group = perm.info.group.intern();
1169 }
1170
1171 perm.info.descriptionRes = sa.getResourceId(
1172 com.android.internal.R.styleable.AndroidManifestPermission_description,
1173 0);
1174
1175 perm.info.protectionLevel = sa.getInt(
1176 com.android.internal.R.styleable.AndroidManifestPermission_protectionLevel,
1177 PermissionInfo.PROTECTION_NORMAL);
1178
1179 sa.recycle();
1180
1181 if (perm.info.protectionLevel == -1) {
1182 outError[0] = "<permission> does not specify protectionLevel";
1183 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1184 return null;
1185 }
1186
1187 if (!parseAllMetaData(res, parser, attrs, "<permission>", perm,
1188 outError)) {
1189 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1190 return null;
1191 }
1192
1193 owner.permissions.add(perm);
1194
1195 return perm;
1196 }
1197
1198 private Permission parsePermissionTree(Package owner, Resources res,
1199 XmlPullParser parser, AttributeSet attrs, String[] outError)
1200 throws XmlPullParserException, IOException {
1201 Permission perm = new Permission(owner);
1202
1203 TypedArray sa = res.obtainAttributes(attrs,
1204 com.android.internal.R.styleable.AndroidManifestPermissionTree);
1205
1206 if (!parsePackageItemInfo(owner, perm.info, outError,
1207 "<permission-tree>", sa,
1208 com.android.internal.R.styleable.AndroidManifestPermissionTree_name,
1209 com.android.internal.R.styleable.AndroidManifestPermissionTree_label,
1210 com.android.internal.R.styleable.AndroidManifestPermissionTree_icon)) {
1211 sa.recycle();
1212 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1213 return null;
1214 }
1215
1216 sa.recycle();
1217
1218 int index = perm.info.name.indexOf('.');
1219 if (index > 0) {
1220 index = perm.info.name.indexOf('.', index+1);
1221 }
1222 if (index < 0) {
1223 outError[0] = "<permission-tree> name has less than three segments: "
1224 + perm.info.name;
1225 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1226 return null;
1227 }
1228
1229 perm.info.descriptionRes = 0;
1230 perm.info.protectionLevel = PermissionInfo.PROTECTION_NORMAL;
1231 perm.tree = true;
1232
1233 if (!parseAllMetaData(res, parser, attrs, "<permission-tree>", perm,
1234 outError)) {
1235 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1236 return null;
1237 }
1238
1239 owner.permissions.add(perm);
1240
1241 return perm;
1242 }
1243
1244 private Instrumentation parseInstrumentation(Package owner, Resources res,
1245 XmlPullParser parser, AttributeSet attrs, String[] outError)
1246 throws XmlPullParserException, IOException {
1247 TypedArray sa = res.obtainAttributes(attrs,
1248 com.android.internal.R.styleable.AndroidManifestInstrumentation);
1249
Dianne Hackborn1d442e02009-04-20 18:14:05 -07001250 if (mParseInstrumentationArgs == null) {
1251 mParseInstrumentationArgs = new ParsePackageItemArgs(owner, outError,
1252 com.android.internal.R.styleable.AndroidManifestInstrumentation_name,
1253 com.android.internal.R.styleable.AndroidManifestInstrumentation_label,
1254 com.android.internal.R.styleable.AndroidManifestInstrumentation_icon);
1255 mParseInstrumentationArgs.tag = "<instrumentation>";
1256 }
1257
1258 mParseInstrumentationArgs.sa = sa;
1259
1260 Instrumentation a = new Instrumentation(mParseInstrumentationArgs,
1261 new InstrumentationInfo());
1262 if (outError[0] != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001263 sa.recycle();
1264 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1265 return null;
1266 }
1267
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001268 String str;
1269 str = sa.getNonResourceString(
1270 com.android.internal.R.styleable.AndroidManifestInstrumentation_targetPackage);
1271 a.info.targetPackage = str != null ? str.intern() : null;
1272
1273 a.info.handleProfiling = sa.getBoolean(
1274 com.android.internal.R.styleable.AndroidManifestInstrumentation_handleProfiling,
1275 false);
1276
1277 a.info.functionalTest = sa.getBoolean(
1278 com.android.internal.R.styleable.AndroidManifestInstrumentation_functionalTest,
1279 false);
1280
1281 sa.recycle();
1282
1283 if (a.info.targetPackage == null) {
1284 outError[0] = "<instrumentation> does not specify targetPackage";
1285 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1286 return null;
1287 }
1288
1289 if (!parseAllMetaData(res, parser, attrs, "<instrumentation>", a,
1290 outError)) {
1291 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1292 return null;
1293 }
1294
1295 owner.instrumentation.add(a);
1296
1297 return a;
1298 }
1299
1300 private boolean parseApplication(Package owner, Resources res,
1301 XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
1302 throws XmlPullParserException, IOException {
1303 final ApplicationInfo ai = owner.applicationInfo;
1304 final String pkgName = owner.applicationInfo.packageName;
1305
1306 TypedArray sa = res.obtainAttributes(attrs,
1307 com.android.internal.R.styleable.AndroidManifestApplication);
1308
1309 String name = sa.getNonResourceString(
1310 com.android.internal.R.styleable.AndroidManifestApplication_name);
1311 if (name != null) {
1312 ai.className = buildClassName(pkgName, name, outError);
1313 if (ai.className == null) {
1314 sa.recycle();
1315 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1316 return false;
1317 }
1318 }
1319
1320 String manageSpaceActivity = sa.getNonResourceString(
1321 com.android.internal.R.styleable.AndroidManifestApplication_manageSpaceActivity);
1322 if (manageSpaceActivity != null) {
1323 ai.manageSpaceActivityName = buildClassName(pkgName, manageSpaceActivity,
1324 outError);
1325 }
1326
Christopher Tate181fafa2009-05-14 11:12:14 -07001327 boolean allowBackup = sa.getBoolean(
1328 com.android.internal.R.styleable.AndroidManifestApplication_allowBackup, true);
1329 if (allowBackup) {
1330 ai.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
Christopher Tate5e1ab332009-09-01 20:32:49 -07001331
1332 // backupAgent, killAfterRestore, and restoreNeedsApplication are only relevant
1333 // if backup is possible for the given application.
Christopher Tate181fafa2009-05-14 11:12:14 -07001334 String backupAgent = sa.getNonResourceString(
1335 com.android.internal.R.styleable.AndroidManifestApplication_backupAgent);
1336 if (backupAgent != null) {
1337 ai.backupAgentName = buildClassName(pkgName, backupAgent, outError);
1338 Log.v(TAG, "android:backupAgent = " + ai.backupAgentName
1339 + " from " + pkgName + "+" + backupAgent);
Christopher Tate5e1ab332009-09-01 20:32:49 -07001340
1341 if (sa.getBoolean(
1342 com.android.internal.R.styleable.AndroidManifestApplication_killAfterRestore,
1343 true)) {
1344 ai.flags |= ApplicationInfo.FLAG_KILL_AFTER_RESTORE;
1345 }
1346 if (sa.getBoolean(
1347 com.android.internal.R.styleable.AndroidManifestApplication_restoreNeedsApplication,
1348 false)) {
1349 ai.flags |= ApplicationInfo.FLAG_RESTORE_NEEDS_APPLICATION;
1350 }
Christopher Tate181fafa2009-05-14 11:12:14 -07001351 }
1352 }
1353
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001354 TypedValue v = sa.peekValue(
1355 com.android.internal.R.styleable.AndroidManifestApplication_label);
1356 if (v != null && (ai.labelRes=v.resourceId) == 0) {
1357 ai.nonLocalizedLabel = v.coerceToString();
1358 }
1359
1360 ai.icon = sa.getResourceId(
1361 com.android.internal.R.styleable.AndroidManifestApplication_icon, 0);
1362 ai.theme = sa.getResourceId(
1363 com.android.internal.R.styleable.AndroidManifestApplication_theme, 0);
1364 ai.descriptionRes = sa.getResourceId(
1365 com.android.internal.R.styleable.AndroidManifestApplication_description, 0);
1366
1367 if ((flags&PARSE_IS_SYSTEM) != 0) {
1368 if (sa.getBoolean(
1369 com.android.internal.R.styleable.AndroidManifestApplication_persistent,
1370 false)) {
1371 ai.flags |= ApplicationInfo.FLAG_PERSISTENT;
1372 }
1373 }
1374
1375 if (sa.getBoolean(
1376 com.android.internal.R.styleable.AndroidManifestApplication_debuggable,
1377 false)) {
1378 ai.flags |= ApplicationInfo.FLAG_DEBUGGABLE;
1379 }
1380
1381 if (sa.getBoolean(
1382 com.android.internal.R.styleable.AndroidManifestApplication_hasCode,
1383 true)) {
1384 ai.flags |= ApplicationInfo.FLAG_HAS_CODE;
1385 }
1386
1387 if (sa.getBoolean(
1388 com.android.internal.R.styleable.AndroidManifestApplication_allowTaskReparenting,
1389 false)) {
1390 ai.flags |= ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING;
1391 }
1392
1393 if (sa.getBoolean(
1394 com.android.internal.R.styleable.AndroidManifestApplication_allowClearUserData,
1395 true)) {
1396 ai.flags |= ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA;
1397 }
1398
Dianne Hackbornade3eca2009-05-11 18:54:45 -07001399 if (sa.getBoolean(
1400 com.android.internal.R.styleable.AndroidManifestApplication_testOnly,
Dianne Hackborne7fe35b2009-05-13 10:53:41 -07001401 false)) {
Dianne Hackbornade3eca2009-05-11 18:54:45 -07001402 ai.flags |= ApplicationInfo.FLAG_TEST_ONLY;
1403 }
1404
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001405 String str;
1406 str = sa.getNonResourceString(
1407 com.android.internal.R.styleable.AndroidManifestApplication_permission);
1408 ai.permission = (str != null && str.length() > 0) ? str.intern() : null;
1409
1410 str = sa.getNonResourceString(
1411 com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity);
1412 ai.taskAffinity = buildTaskAffinityName(ai.packageName, ai.packageName,
1413 str, outError);
1414
1415 if (outError[0] == null) {
1416 ai.processName = buildProcessName(ai.packageName, null, sa.getNonResourceString(
1417 com.android.internal.R.styleable.AndroidManifestApplication_process),
1418 flags, mSeparateProcesses, outError);
1419
1420 ai.enabled = sa.getBoolean(com.android.internal.R.styleable.AndroidManifestApplication_enabled, true);
1421 }
1422
1423 sa.recycle();
1424
1425 if (outError[0] != null) {
1426 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1427 return false;
1428 }
1429
1430 final int innerDepth = parser.getDepth();
1431
1432 int type;
1433 while ((type=parser.next()) != parser.END_DOCUMENT
1434 && (type != parser.END_TAG || parser.getDepth() > innerDepth)) {
1435 if (type == parser.END_TAG || type == parser.TEXT) {
1436 continue;
1437 }
1438
1439 String tagName = parser.getName();
1440 if (tagName.equals("activity")) {
1441 Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false);
1442 if (a == null) {
1443 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1444 return false;
1445 }
1446
1447 owner.activities.add(a);
1448
1449 } else if (tagName.equals("receiver")) {
1450 Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true);
1451 if (a == null) {
1452 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1453 return false;
1454 }
1455
1456 owner.receivers.add(a);
1457
1458 } else if (tagName.equals("service")) {
1459 Service s = parseService(owner, res, parser, attrs, flags, outError);
1460 if (s == null) {
1461 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1462 return false;
1463 }
1464
1465 owner.services.add(s);
1466
1467 } else if (tagName.equals("provider")) {
1468 Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
1469 if (p == null) {
1470 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1471 return false;
1472 }
1473
1474 owner.providers.add(p);
1475
1476 } else if (tagName.equals("activity-alias")) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -07001477 Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001478 if (a == null) {
1479 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1480 return false;
1481 }
1482
1483 owner.activities.add(a);
1484
1485 } else if (parser.getName().equals("meta-data")) {
1486 // note: application meta-data is stored off to the side, so it can
1487 // remain null in the primary copy (we like to avoid extra copies because
1488 // it can be large)
1489 if ((owner.mAppMetaData = parseMetaData(res, parser, attrs, owner.mAppMetaData,
1490 outError)) == null) {
1491 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1492 return false;
1493 }
1494
1495 } else if (tagName.equals("uses-library")) {
1496 sa = res.obtainAttributes(attrs,
1497 com.android.internal.R.styleable.AndroidManifestUsesLibrary);
1498
1499 String lname = sa.getNonResourceString(
1500 com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
Dianne Hackborn49237342009-08-27 20:08:01 -07001501 boolean req = sa.getBoolean(
1502 com.android.internal.R.styleable.AndroidManifestUsesLibrary_required,
1503 true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001504
1505 sa.recycle();
1506
Dianne Hackborn49237342009-08-27 20:08:01 -07001507 if (lname != null) {
1508 if (req) {
1509 if (owner.usesLibraries == null) {
1510 owner.usesLibraries = new ArrayList<String>();
1511 }
1512 if (!owner.usesLibraries.contains(lname)) {
1513 owner.usesLibraries.add(lname.intern());
1514 }
1515 } else {
1516 if (owner.usesOptionalLibraries == null) {
1517 owner.usesOptionalLibraries = new ArrayList<String>();
1518 }
1519 if (!owner.usesOptionalLibraries.contains(lname)) {
1520 owner.usesOptionalLibraries.add(lname.intern());
1521 }
1522 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001523 }
1524
1525 XmlUtils.skipCurrentTag(parser);
1526
1527 } else {
1528 if (!RIGID_PARSER) {
1529 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
1530 Log.w(TAG, "Unknown element under <application>: " + tagName);
1531 XmlUtils.skipCurrentTag(parser);
1532 continue;
1533 } else {
1534 outError[0] = "Bad element under <application>: " + tagName;
1535 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1536 return false;
1537 }
1538 }
1539 }
1540
1541 return true;
1542 }
1543
1544 private boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo,
1545 String[] outError, String tag, TypedArray sa,
1546 int nameRes, int labelRes, int iconRes) {
1547 String name = sa.getNonResourceString(nameRes);
1548 if (name == null) {
1549 outError[0] = tag + " does not specify android:name";
1550 return false;
1551 }
1552
1553 outInfo.name
1554 = buildClassName(owner.applicationInfo.packageName, name, outError);
1555 if (outInfo.name == null) {
1556 return false;
1557 }
1558
1559 int iconVal = sa.getResourceId(iconRes, 0);
1560 if (iconVal != 0) {
1561 outInfo.icon = iconVal;
1562 outInfo.nonLocalizedLabel = null;
1563 }
1564
1565 TypedValue v = sa.peekValue(labelRes);
1566 if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
1567 outInfo.nonLocalizedLabel = v.coerceToString();
1568 }
1569
1570 outInfo.packageName = owner.packageName;
1571
1572 return true;
1573 }
1574
1575 private boolean parseComponentInfo(Package owner, int flags,
1576 ComponentInfo outInfo, String[] outError, String tag, TypedArray sa,
1577 int nameRes, int labelRes, int iconRes, int processRes,
1578 int enabledRes) {
1579 if (!parsePackageItemInfo(owner, outInfo, outError, tag, sa,
1580 nameRes, labelRes, iconRes)) {
1581 return false;
1582 }
1583
1584 if (processRes != 0) {
1585 outInfo.processName = buildProcessName(owner.applicationInfo.packageName,
1586 owner.applicationInfo.processName, sa.getNonResourceString(processRes),
1587 flags, mSeparateProcesses, outError);
1588 }
1589 outInfo.enabled = sa.getBoolean(enabledRes, true);
1590
1591 return outError[0] == null;
1592 }
Dianne Hackborn1d442e02009-04-20 18:14:05 -07001593
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001594 private Activity parseActivity(Package owner, Resources res,
1595 XmlPullParser parser, AttributeSet attrs, int flags, String[] outError,
1596 boolean receiver) throws XmlPullParserException, IOException {
1597 TypedArray sa = res.obtainAttributes(attrs,
1598 com.android.internal.R.styleable.AndroidManifestActivity);
1599
Dianne Hackborn1d442e02009-04-20 18:14:05 -07001600 if (mParseActivityArgs == null) {
1601 mParseActivityArgs = new ParseComponentArgs(owner, outError,
1602 com.android.internal.R.styleable.AndroidManifestActivity_name,
1603 com.android.internal.R.styleable.AndroidManifestActivity_label,
1604 com.android.internal.R.styleable.AndroidManifestActivity_icon,
1605 mSeparateProcesses,
1606 com.android.internal.R.styleable.AndroidManifestActivity_process,
1607 com.android.internal.R.styleable.AndroidManifestActivity_enabled);
1608 }
1609
1610 mParseActivityArgs.tag = receiver ? "<receiver>" : "<activity>";
1611 mParseActivityArgs.sa = sa;
1612 mParseActivityArgs.flags = flags;
1613
1614 Activity a = new Activity(mParseActivityArgs, new ActivityInfo());
1615 if (outError[0] != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001616 sa.recycle();
1617 return null;
1618 }
1619
1620 final boolean setExported = sa.hasValue(
1621 com.android.internal.R.styleable.AndroidManifestActivity_exported);
1622 if (setExported) {
1623 a.info.exported = sa.getBoolean(
1624 com.android.internal.R.styleable.AndroidManifestActivity_exported, false);
1625 }
1626
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001627 a.info.theme = sa.getResourceId(
1628 com.android.internal.R.styleable.AndroidManifestActivity_theme, 0);
1629
1630 String str;
1631 str = sa.getNonResourceString(
1632 com.android.internal.R.styleable.AndroidManifestActivity_permission);
1633 if (str == null) {
1634 a.info.permission = owner.applicationInfo.permission;
1635 } else {
1636 a.info.permission = str.length() > 0 ? str.toString().intern() : null;
1637 }
1638
1639 str = sa.getNonResourceString(
1640 com.android.internal.R.styleable.AndroidManifestActivity_taskAffinity);
1641 a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName,
1642 owner.applicationInfo.taskAffinity, str, outError);
1643
1644 a.info.flags = 0;
1645 if (sa.getBoolean(
1646 com.android.internal.R.styleable.AndroidManifestActivity_multiprocess,
1647 false)) {
1648 a.info.flags |= ActivityInfo.FLAG_MULTIPROCESS;
1649 }
1650
1651 if (sa.getBoolean(
1652 com.android.internal.R.styleable.AndroidManifestActivity_finishOnTaskLaunch,
1653 false)) {
1654 a.info.flags |= ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH;
1655 }
1656
1657 if (sa.getBoolean(
1658 com.android.internal.R.styleable.AndroidManifestActivity_clearTaskOnLaunch,
1659 false)) {
1660 a.info.flags |= ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH;
1661 }
1662
1663 if (sa.getBoolean(
1664 com.android.internal.R.styleable.AndroidManifestActivity_noHistory,
1665 false)) {
1666 a.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
1667 }
1668
1669 if (sa.getBoolean(
1670 com.android.internal.R.styleable.AndroidManifestActivity_alwaysRetainTaskState,
1671 false)) {
1672 a.info.flags |= ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE;
1673 }
1674
1675 if (sa.getBoolean(
1676 com.android.internal.R.styleable.AndroidManifestActivity_stateNotNeeded,
1677 false)) {
1678 a.info.flags |= ActivityInfo.FLAG_STATE_NOT_NEEDED;
1679 }
1680
1681 if (sa.getBoolean(
1682 com.android.internal.R.styleable.AndroidManifestActivity_excludeFromRecents,
1683 false)) {
1684 a.info.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
1685 }
1686
1687 if (sa.getBoolean(
1688 com.android.internal.R.styleable.AndroidManifestActivity_allowTaskReparenting,
1689 (owner.applicationInfo.flags&ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING) != 0)) {
1690 a.info.flags |= ActivityInfo.FLAG_ALLOW_TASK_REPARENTING;
1691 }
1692
Dianne Hackbornffa42482009-09-23 22:20:11 -07001693 if (sa.getBoolean(
1694 com.android.internal.R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs,
1695 false)) {
1696 a.info.flags |= ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
1697 }
1698
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001699 if (!receiver) {
1700 a.info.launchMode = sa.getInt(
1701 com.android.internal.R.styleable.AndroidManifestActivity_launchMode,
1702 ActivityInfo.LAUNCH_MULTIPLE);
1703 a.info.screenOrientation = sa.getInt(
1704 com.android.internal.R.styleable.AndroidManifestActivity_screenOrientation,
1705 ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1706 a.info.configChanges = sa.getInt(
1707 com.android.internal.R.styleable.AndroidManifestActivity_configChanges,
1708 0);
1709 a.info.softInputMode = sa.getInt(
1710 com.android.internal.R.styleable.AndroidManifestActivity_windowSoftInputMode,
1711 0);
1712 } else {
1713 a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
1714 a.info.configChanges = 0;
1715 }
1716
1717 sa.recycle();
1718
1719 if (outError[0] != null) {
1720 return null;
1721 }
1722
1723 int outerDepth = parser.getDepth();
1724 int type;
1725 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1726 && (type != XmlPullParser.END_TAG
1727 || parser.getDepth() > outerDepth)) {
1728 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1729 continue;
1730 }
1731
1732 if (parser.getName().equals("intent-filter")) {
1733 ActivityIntentInfo intent = new ActivityIntentInfo(a);
1734 if (!parseIntent(res, parser, attrs, flags, intent, outError, !receiver)) {
1735 return null;
1736 }
1737 if (intent.countActions() == 0) {
1738 Log.w(TAG, "Intent filter for activity " + intent
1739 + " defines no actions");
1740 } else {
1741 a.intents.add(intent);
1742 }
1743 } else if (parser.getName().equals("meta-data")) {
1744 if ((a.metaData=parseMetaData(res, parser, attrs, a.metaData,
1745 outError)) == null) {
1746 return null;
1747 }
1748 } else {
1749 if (!RIGID_PARSER) {
1750 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
1751 if (receiver) {
1752 Log.w(TAG, "Unknown element under <receiver>: " + parser.getName());
1753 } else {
1754 Log.w(TAG, "Unknown element under <activity>: " + parser.getName());
1755 }
1756 XmlUtils.skipCurrentTag(parser);
1757 continue;
1758 }
1759 if (receiver) {
1760 outError[0] = "Bad element under <receiver>: " + parser.getName();
1761 } else {
1762 outError[0] = "Bad element under <activity>: " + parser.getName();
1763 }
1764 return null;
1765 }
1766 }
1767
1768 if (!setExported) {
1769 a.info.exported = a.intents.size() > 0;
1770 }
1771
1772 return a;
1773 }
1774
1775 private Activity parseActivityAlias(Package owner, Resources res,
Dianne Hackborn1d442e02009-04-20 18:14:05 -07001776 XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
1777 throws XmlPullParserException, IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001778 TypedArray sa = res.obtainAttributes(attrs,
1779 com.android.internal.R.styleable.AndroidManifestActivityAlias);
1780
1781 String targetActivity = sa.getNonResourceString(
1782 com.android.internal.R.styleable.AndroidManifestActivityAlias_targetActivity);
1783 if (targetActivity == null) {
1784 outError[0] = "<activity-alias> does not specify android:targetActivity";
1785 sa.recycle();
1786 return null;
1787 }
1788
1789 targetActivity = buildClassName(owner.applicationInfo.packageName,
1790 targetActivity, outError);
1791 if (targetActivity == null) {
1792 sa.recycle();
1793 return null;
1794 }
1795
Dianne Hackborn1d442e02009-04-20 18:14:05 -07001796 if (mParseActivityAliasArgs == null) {
1797 mParseActivityAliasArgs = new ParseComponentArgs(owner, outError,
1798 com.android.internal.R.styleable.AndroidManifestActivityAlias_name,
1799 com.android.internal.R.styleable.AndroidManifestActivityAlias_label,
1800 com.android.internal.R.styleable.AndroidManifestActivityAlias_icon,
1801 mSeparateProcesses,
1802 0,
1803 com.android.internal.R.styleable.AndroidManifestActivityAlias_enabled);
1804 mParseActivityAliasArgs.tag = "<activity-alias>";
1805 }
1806
1807 mParseActivityAliasArgs.sa = sa;
1808 mParseActivityAliasArgs.flags = flags;
1809
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001810 Activity target = null;
1811
1812 final int NA = owner.activities.size();
1813 for (int i=0; i<NA; i++) {
1814 Activity t = owner.activities.get(i);
1815 if (targetActivity.equals(t.info.name)) {
1816 target = t;
1817 break;
1818 }
1819 }
1820
1821 if (target == null) {
1822 outError[0] = "<activity-alias> target activity " + targetActivity
1823 + " not found in manifest";
1824 sa.recycle();
1825 return null;
1826 }
1827
Dianne Hackborn1d442e02009-04-20 18:14:05 -07001828 ActivityInfo info = new ActivityInfo();
1829 info.targetActivity = targetActivity;
1830 info.configChanges = target.info.configChanges;
1831 info.flags = target.info.flags;
1832 info.icon = target.info.icon;
1833 info.labelRes = target.info.labelRes;
1834 info.nonLocalizedLabel = target.info.nonLocalizedLabel;
1835 info.launchMode = target.info.launchMode;
1836 info.processName = target.info.processName;
1837 info.screenOrientation = target.info.screenOrientation;
1838 info.taskAffinity = target.info.taskAffinity;
1839 info.theme = target.info.theme;
1840
1841 Activity a = new Activity(mParseActivityAliasArgs, info);
1842 if (outError[0] != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001843 sa.recycle();
1844 return null;
1845 }
1846
1847 final boolean setExported = sa.hasValue(
1848 com.android.internal.R.styleable.AndroidManifestActivityAlias_exported);
1849 if (setExported) {
1850 a.info.exported = sa.getBoolean(
1851 com.android.internal.R.styleable.AndroidManifestActivityAlias_exported, false);
1852 }
1853
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001854 String str;
1855 str = sa.getNonResourceString(
1856 com.android.internal.R.styleable.AndroidManifestActivityAlias_permission);
1857 if (str != null) {
1858 a.info.permission = str.length() > 0 ? str.toString().intern() : null;
1859 }
1860
1861 sa.recycle();
1862
1863 if (outError[0] != null) {
1864 return null;
1865 }
1866
1867 int outerDepth = parser.getDepth();
1868 int type;
1869 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1870 && (type != XmlPullParser.END_TAG
1871 || parser.getDepth() > outerDepth)) {
1872 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1873 continue;
1874 }
1875
1876 if (parser.getName().equals("intent-filter")) {
1877 ActivityIntentInfo intent = new ActivityIntentInfo(a);
1878 if (!parseIntent(res, parser, attrs, flags, intent, outError, true)) {
1879 return null;
1880 }
1881 if (intent.countActions() == 0) {
1882 Log.w(TAG, "Intent filter for activity alias " + intent
1883 + " defines no actions");
1884 } else {
1885 a.intents.add(intent);
1886 }
1887 } else if (parser.getName().equals("meta-data")) {
1888 if ((a.metaData=parseMetaData(res, parser, attrs, a.metaData,
1889 outError)) == null) {
1890 return null;
1891 }
1892 } else {
1893 if (!RIGID_PARSER) {
1894 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
1895 Log.w(TAG, "Unknown element under <activity-alias>: " + parser.getName());
1896 XmlUtils.skipCurrentTag(parser);
1897 continue;
1898 }
1899 outError[0] = "Bad element under <activity-alias>: " + parser.getName();
1900 return null;
1901 }
1902 }
1903
1904 if (!setExported) {
1905 a.info.exported = a.intents.size() > 0;
1906 }
1907
1908 return a;
1909 }
1910
1911 private Provider parseProvider(Package owner, Resources res,
1912 XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
1913 throws XmlPullParserException, IOException {
1914 TypedArray sa = res.obtainAttributes(attrs,
1915 com.android.internal.R.styleable.AndroidManifestProvider);
1916
Dianne Hackborn1d442e02009-04-20 18:14:05 -07001917 if (mParseProviderArgs == null) {
1918 mParseProviderArgs = new ParseComponentArgs(owner, outError,
1919 com.android.internal.R.styleable.AndroidManifestProvider_name,
1920 com.android.internal.R.styleable.AndroidManifestProvider_label,
1921 com.android.internal.R.styleable.AndroidManifestProvider_icon,
1922 mSeparateProcesses,
1923 com.android.internal.R.styleable.AndroidManifestProvider_process,
1924 com.android.internal.R.styleable.AndroidManifestProvider_enabled);
1925 mParseProviderArgs.tag = "<provider>";
1926 }
1927
1928 mParseProviderArgs.sa = sa;
1929 mParseProviderArgs.flags = flags;
1930
1931 Provider p = new Provider(mParseProviderArgs, new ProviderInfo());
1932 if (outError[0] != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001933 sa.recycle();
1934 return null;
1935 }
1936
1937 p.info.exported = sa.getBoolean(
1938 com.android.internal.R.styleable.AndroidManifestProvider_exported, true);
1939
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001940 String cpname = sa.getNonResourceString(
1941 com.android.internal.R.styleable.AndroidManifestProvider_authorities);
1942
1943 p.info.isSyncable = sa.getBoolean(
1944 com.android.internal.R.styleable.AndroidManifestProvider_syncable,
1945 false);
1946
1947 String permission = sa.getNonResourceString(
1948 com.android.internal.R.styleable.AndroidManifestProvider_permission);
1949 String str = sa.getNonResourceString(
1950 com.android.internal.R.styleable.AndroidManifestProvider_readPermission);
1951 if (str == null) {
1952 str = permission;
1953 }
1954 if (str == null) {
1955 p.info.readPermission = owner.applicationInfo.permission;
1956 } else {
1957 p.info.readPermission =
1958 str.length() > 0 ? str.toString().intern() : null;
1959 }
1960 str = sa.getNonResourceString(
1961 com.android.internal.R.styleable.AndroidManifestProvider_writePermission);
1962 if (str == null) {
1963 str = permission;
1964 }
1965 if (str == null) {
1966 p.info.writePermission = owner.applicationInfo.permission;
1967 } else {
1968 p.info.writePermission =
1969 str.length() > 0 ? str.toString().intern() : null;
1970 }
1971
1972 p.info.grantUriPermissions = sa.getBoolean(
1973 com.android.internal.R.styleable.AndroidManifestProvider_grantUriPermissions,
1974 false);
1975
1976 p.info.multiprocess = sa.getBoolean(
1977 com.android.internal.R.styleable.AndroidManifestProvider_multiprocess,
1978 false);
1979
1980 p.info.initOrder = sa.getInt(
1981 com.android.internal.R.styleable.AndroidManifestProvider_initOrder,
1982 0);
1983
1984 sa.recycle();
1985
1986 if (cpname == null) {
1987 outError[0] = "<provider> does not incude authorities attribute";
1988 return null;
1989 }
1990 p.info.authority = cpname.intern();
1991
1992 if (!parseProviderTags(res, parser, attrs, p, outError)) {
1993 return null;
1994 }
1995
1996 return p;
1997 }
1998
1999 private boolean parseProviderTags(Resources res,
2000 XmlPullParser parser, AttributeSet attrs,
2001 Provider outInfo, String[] outError)
2002 throws XmlPullParserException, IOException {
2003 int outerDepth = parser.getDepth();
2004 int type;
2005 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
2006 && (type != XmlPullParser.END_TAG
2007 || parser.getDepth() > outerDepth)) {
2008 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
2009 continue;
2010 }
2011
2012 if (parser.getName().equals("meta-data")) {
2013 if ((outInfo.metaData=parseMetaData(res, parser, attrs,
2014 outInfo.metaData, outError)) == null) {
2015 return false;
2016 }
Dianne Hackborn2af632f2009-07-08 14:56:37 -07002017
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002018 } else if (parser.getName().equals("grant-uri-permission")) {
2019 TypedArray sa = res.obtainAttributes(attrs,
2020 com.android.internal.R.styleable.AndroidManifestGrantUriPermission);
2021
2022 PatternMatcher pa = null;
2023
2024 String str = sa.getNonResourceString(
2025 com.android.internal.R.styleable.AndroidManifestGrantUriPermission_path);
2026 if (str != null) {
2027 pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL);
2028 }
2029
2030 str = sa.getNonResourceString(
2031 com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPrefix);
2032 if (str != null) {
2033 pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX);
2034 }
2035
2036 str = sa.getNonResourceString(
2037 com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPattern);
2038 if (str != null) {
2039 pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
2040 }
Dianne Hackborn2af632f2009-07-08 14:56:37 -07002041
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002042 sa.recycle();
2043
2044 if (pa != null) {
2045 if (outInfo.info.uriPermissionPatterns == null) {
2046 outInfo.info.uriPermissionPatterns = new PatternMatcher[1];
2047 outInfo.info.uriPermissionPatterns[0] = pa;
2048 } else {
2049 final int N = outInfo.info.uriPermissionPatterns.length;
2050 PatternMatcher[] newp = new PatternMatcher[N+1];
2051 System.arraycopy(outInfo.info.uriPermissionPatterns, 0, newp, 0, N);
2052 newp[N] = pa;
2053 outInfo.info.uriPermissionPatterns = newp;
2054 }
2055 outInfo.info.grantUriPermissions = true;
Dianne Hackborn2af632f2009-07-08 14:56:37 -07002056 } else {
2057 if (!RIGID_PARSER) {
2058 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
2059 Log.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>");
2060 XmlUtils.skipCurrentTag(parser);
2061 continue;
2062 }
2063 outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>";
2064 return false;
2065 }
2066 XmlUtils.skipCurrentTag(parser);
2067
2068 } else if (parser.getName().equals("path-permission")) {
2069 TypedArray sa = res.obtainAttributes(attrs,
2070 com.android.internal.R.styleable.AndroidManifestPathPermission);
2071
2072 PathPermission pa = null;
2073
2074 String permission = sa.getNonResourceString(
2075 com.android.internal.R.styleable.AndroidManifestPathPermission_permission);
2076 String readPermission = sa.getNonResourceString(
2077 com.android.internal.R.styleable.AndroidManifestPathPermission_readPermission);
2078 if (readPermission == null) {
2079 readPermission = permission;
2080 }
2081 String writePermission = sa.getNonResourceString(
2082 com.android.internal.R.styleable.AndroidManifestPathPermission_writePermission);
2083 if (writePermission == null) {
2084 writePermission = permission;
2085 }
2086
2087 boolean havePerm = false;
2088 if (readPermission != null) {
2089 readPermission = readPermission.intern();
2090 havePerm = true;
2091 }
2092 if (writePermission != null) {
2093 writePermission = readPermission.intern();
2094 havePerm = true;
2095 }
2096
2097 if (!havePerm) {
2098 if (!RIGID_PARSER) {
2099 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
2100 Log.w(TAG, "No readPermission or writePermssion for <path-permission>");
2101 XmlUtils.skipCurrentTag(parser);
2102 continue;
2103 }
2104 outError[0] = "No readPermission or writePermssion for <path-permission>";
2105 return false;
2106 }
2107
2108 String path = sa.getNonResourceString(
2109 com.android.internal.R.styleable.AndroidManifestPathPermission_path);
2110 if (path != null) {
2111 pa = new PathPermission(path,
2112 PatternMatcher.PATTERN_LITERAL, readPermission, writePermission);
2113 }
2114
2115 path = sa.getNonResourceString(
2116 com.android.internal.R.styleable.AndroidManifestPathPermission_pathPrefix);
2117 if (path != null) {
2118 pa = new PathPermission(path,
2119 PatternMatcher.PATTERN_PREFIX, readPermission, writePermission);
2120 }
2121
2122 path = sa.getNonResourceString(
2123 com.android.internal.R.styleable.AndroidManifestPathPermission_pathPattern);
2124 if (path != null) {
2125 pa = new PathPermission(path,
2126 PatternMatcher.PATTERN_SIMPLE_GLOB, readPermission, writePermission);
2127 }
2128
2129 sa.recycle();
2130
2131 if (pa != null) {
2132 if (outInfo.info.pathPermissions == null) {
2133 outInfo.info.pathPermissions = new PathPermission[1];
2134 outInfo.info.pathPermissions[0] = pa;
2135 } else {
2136 final int N = outInfo.info.pathPermissions.length;
2137 PathPermission[] newp = new PathPermission[N+1];
2138 System.arraycopy(outInfo.info.pathPermissions, 0, newp, 0, N);
2139 newp[N] = pa;
2140 outInfo.info.pathPermissions = newp;
2141 }
2142 } else {
2143 if (!RIGID_PARSER) {
2144 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
2145 Log.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>");
2146 XmlUtils.skipCurrentTag(parser);
2147 continue;
2148 }
2149 outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>";
2150 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002151 }
2152 XmlUtils.skipCurrentTag(parser);
2153
2154 } else {
2155 if (!RIGID_PARSER) {
2156 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
2157 Log.w(TAG, "Unknown element under <provider>: "
2158 + parser.getName());
2159 XmlUtils.skipCurrentTag(parser);
2160 continue;
2161 }
2162 outError[0] = "Bad element under <provider>: "
2163 + parser.getName();
2164 return false;
2165 }
2166 }
2167 return true;
2168 }
2169
2170 private Service parseService(Package owner, Resources res,
2171 XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
2172 throws XmlPullParserException, IOException {
2173 TypedArray sa = res.obtainAttributes(attrs,
2174 com.android.internal.R.styleable.AndroidManifestService);
2175
Dianne Hackborn1d442e02009-04-20 18:14:05 -07002176 if (mParseServiceArgs == null) {
2177 mParseServiceArgs = new ParseComponentArgs(owner, outError,
2178 com.android.internal.R.styleable.AndroidManifestService_name,
2179 com.android.internal.R.styleable.AndroidManifestService_label,
2180 com.android.internal.R.styleable.AndroidManifestService_icon,
2181 mSeparateProcesses,
2182 com.android.internal.R.styleable.AndroidManifestService_process,
2183 com.android.internal.R.styleable.AndroidManifestService_enabled);
2184 mParseServiceArgs.tag = "<service>";
2185 }
2186
2187 mParseServiceArgs.sa = sa;
2188 mParseServiceArgs.flags = flags;
2189
2190 Service s = new Service(mParseServiceArgs, new ServiceInfo());
2191 if (outError[0] != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002192 sa.recycle();
2193 return null;
2194 }
2195
2196 final boolean setExported = sa.hasValue(
2197 com.android.internal.R.styleable.AndroidManifestService_exported);
2198 if (setExported) {
2199 s.info.exported = sa.getBoolean(
2200 com.android.internal.R.styleable.AndroidManifestService_exported, false);
2201 }
2202
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002203 String str = sa.getNonResourceString(
2204 com.android.internal.R.styleable.AndroidManifestService_permission);
2205 if (str == null) {
2206 s.info.permission = owner.applicationInfo.permission;
2207 } else {
2208 s.info.permission = str.length() > 0 ? str.toString().intern() : null;
2209 }
2210
2211 sa.recycle();
2212
2213 int outerDepth = parser.getDepth();
2214 int type;
2215 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
2216 && (type != XmlPullParser.END_TAG
2217 || parser.getDepth() > outerDepth)) {
2218 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
2219 continue;
2220 }
2221
2222 if (parser.getName().equals("intent-filter")) {
2223 ServiceIntentInfo intent = new ServiceIntentInfo(s);
2224 if (!parseIntent(res, parser, attrs, flags, intent, outError, false)) {
2225 return null;
2226 }
2227
2228 s.intents.add(intent);
2229 } else if (parser.getName().equals("meta-data")) {
2230 if ((s.metaData=parseMetaData(res, parser, attrs, s.metaData,
2231 outError)) == null) {
2232 return null;
2233 }
2234 } else {
2235 if (!RIGID_PARSER) {
2236 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
2237 Log.w(TAG, "Unknown element under <service>: "
2238 + parser.getName());
2239 XmlUtils.skipCurrentTag(parser);
2240 continue;
2241 }
2242 outError[0] = "Bad element under <service>: "
2243 + parser.getName();
2244 return null;
2245 }
2246 }
2247
2248 if (!setExported) {
2249 s.info.exported = s.intents.size() > 0;
2250 }
2251
2252 return s;
2253 }
2254
2255 private boolean parseAllMetaData(Resources res,
2256 XmlPullParser parser, AttributeSet attrs, String tag,
2257 Component outInfo, String[] outError)
2258 throws XmlPullParserException, IOException {
2259 int outerDepth = parser.getDepth();
2260 int type;
2261 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
2262 && (type != XmlPullParser.END_TAG
2263 || parser.getDepth() > outerDepth)) {
2264 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
2265 continue;
2266 }
2267
2268 if (parser.getName().equals("meta-data")) {
2269 if ((outInfo.metaData=parseMetaData(res, parser, attrs,
2270 outInfo.metaData, outError)) == null) {
2271 return false;
2272 }
2273 } else {
2274 if (!RIGID_PARSER) {
2275 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
2276 Log.w(TAG, "Unknown element under " + tag + ": "
2277 + parser.getName());
2278 XmlUtils.skipCurrentTag(parser);
2279 continue;
2280 }
2281 outError[0] = "Bad element under " + tag + ": "
2282 + parser.getName();
2283 return false;
2284 }
2285 }
2286 return true;
2287 }
2288
2289 private Bundle parseMetaData(Resources res,
2290 XmlPullParser parser, AttributeSet attrs,
2291 Bundle data, String[] outError)
2292 throws XmlPullParserException, IOException {
2293
2294 TypedArray sa = res.obtainAttributes(attrs,
2295 com.android.internal.R.styleable.AndroidManifestMetaData);
2296
2297 if (data == null) {
2298 data = new Bundle();
2299 }
2300
2301 String name = sa.getNonResourceString(
2302 com.android.internal.R.styleable.AndroidManifestMetaData_name);
2303 if (name == null) {
2304 outError[0] = "<meta-data> requires an android:name attribute";
2305 sa.recycle();
2306 return null;
2307 }
2308
Dianne Hackborn854060a2009-07-09 18:14:31 -07002309 name = name.intern();
2310
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002311 TypedValue v = sa.peekValue(
2312 com.android.internal.R.styleable.AndroidManifestMetaData_resource);
2313 if (v != null && v.resourceId != 0) {
2314 //Log.i(TAG, "Meta data ref " + name + ": " + v);
2315 data.putInt(name, v.resourceId);
2316 } else {
2317 v = sa.peekValue(
2318 com.android.internal.R.styleable.AndroidManifestMetaData_value);
2319 //Log.i(TAG, "Meta data " + name + ": " + v);
2320 if (v != null) {
2321 if (v.type == TypedValue.TYPE_STRING) {
2322 CharSequence cs = v.coerceToString();
Dianne Hackborn854060a2009-07-09 18:14:31 -07002323 data.putString(name, cs != null ? cs.toString().intern() : null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002324 } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
2325 data.putBoolean(name, v.data != 0);
2326 } else if (v.type >= TypedValue.TYPE_FIRST_INT
2327 && v.type <= TypedValue.TYPE_LAST_INT) {
2328 data.putInt(name, v.data);
2329 } else if (v.type == TypedValue.TYPE_FLOAT) {
2330 data.putFloat(name, v.getFloat());
2331 } else {
2332 if (!RIGID_PARSER) {
2333 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
2334 Log.w(TAG, "<meta-data> only supports string, integer, float, color, boolean, and resource reference types");
2335 } else {
2336 outError[0] = "<meta-data> only supports string, integer, float, color, boolean, and resource reference types";
2337 data = null;
2338 }
2339 }
2340 } else {
2341 outError[0] = "<meta-data> requires an android:value or android:resource attribute";
2342 data = null;
2343 }
2344 }
2345
2346 sa.recycle();
2347
2348 XmlUtils.skipCurrentTag(parser);
2349
2350 return data;
2351 }
2352
2353 private static final String ANDROID_RESOURCES
2354 = "http://schemas.android.com/apk/res/android";
2355
2356 private boolean parseIntent(Resources res,
2357 XmlPullParser parser, AttributeSet attrs, int flags,
2358 IntentInfo outInfo, String[] outError, boolean isActivity)
2359 throws XmlPullParserException, IOException {
2360
2361 TypedArray sa = res.obtainAttributes(attrs,
2362 com.android.internal.R.styleable.AndroidManifestIntentFilter);
2363
2364 int priority = sa.getInt(
2365 com.android.internal.R.styleable.AndroidManifestIntentFilter_priority, 0);
2366 if (priority > 0 && isActivity && (flags&PARSE_IS_SYSTEM) == 0) {
2367 Log.w(TAG, "Activity with priority > 0, forcing to 0 at "
2368 + parser.getPositionDescription());
2369 priority = 0;
2370 }
2371 outInfo.setPriority(priority);
2372
2373 TypedValue v = sa.peekValue(
2374 com.android.internal.R.styleable.AndroidManifestIntentFilter_label);
2375 if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
2376 outInfo.nonLocalizedLabel = v.coerceToString();
2377 }
2378
2379 outInfo.icon = sa.getResourceId(
2380 com.android.internal.R.styleable.AndroidManifestIntentFilter_icon, 0);
2381
2382 sa.recycle();
2383
2384 int outerDepth = parser.getDepth();
2385 int type;
2386 while ((type=parser.next()) != parser.END_DOCUMENT
2387 && (type != parser.END_TAG || parser.getDepth() > outerDepth)) {
2388 if (type == parser.END_TAG || type == parser.TEXT) {
2389 continue;
2390 }
2391
2392 String nodeName = parser.getName();
2393 if (nodeName.equals("action")) {
2394 String value = attrs.getAttributeValue(
2395 ANDROID_RESOURCES, "name");
2396 if (value == null || value == "") {
2397 outError[0] = "No value supplied for <android:name>";
2398 return false;
2399 }
2400 XmlUtils.skipCurrentTag(parser);
2401
2402 outInfo.addAction(value);
2403 } else if (nodeName.equals("category")) {
2404 String value = attrs.getAttributeValue(
2405 ANDROID_RESOURCES, "name");
2406 if (value == null || value == "") {
2407 outError[0] = "No value supplied for <android:name>";
2408 return false;
2409 }
2410 XmlUtils.skipCurrentTag(parser);
2411
2412 outInfo.addCategory(value);
2413
2414 } else if (nodeName.equals("data")) {
2415 sa = res.obtainAttributes(attrs,
2416 com.android.internal.R.styleable.AndroidManifestData);
2417
2418 String str = sa.getNonResourceString(
2419 com.android.internal.R.styleable.AndroidManifestData_mimeType);
2420 if (str != null) {
2421 try {
2422 outInfo.addDataType(str);
2423 } catch (IntentFilter.MalformedMimeTypeException e) {
2424 outError[0] = e.toString();
2425 sa.recycle();
2426 return false;
2427 }
2428 }
2429
2430 str = sa.getNonResourceString(
2431 com.android.internal.R.styleable.AndroidManifestData_scheme);
2432 if (str != null) {
2433 outInfo.addDataScheme(str);
2434 }
2435
2436 String host = sa.getNonResourceString(
2437 com.android.internal.R.styleable.AndroidManifestData_host);
2438 String port = sa.getNonResourceString(
2439 com.android.internal.R.styleable.AndroidManifestData_port);
2440 if (host != null) {
2441 outInfo.addDataAuthority(host, port);
2442 }
2443
2444 str = sa.getNonResourceString(
2445 com.android.internal.R.styleable.AndroidManifestData_path);
2446 if (str != null) {
2447 outInfo.addDataPath(str, PatternMatcher.PATTERN_LITERAL);
2448 }
2449
2450 str = sa.getNonResourceString(
2451 com.android.internal.R.styleable.AndroidManifestData_pathPrefix);
2452 if (str != null) {
2453 outInfo.addDataPath(str, PatternMatcher.PATTERN_PREFIX);
2454 }
2455
2456 str = sa.getNonResourceString(
2457 com.android.internal.R.styleable.AndroidManifestData_pathPattern);
2458 if (str != null) {
2459 outInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
2460 }
2461
2462 sa.recycle();
2463 XmlUtils.skipCurrentTag(parser);
2464 } else if (!RIGID_PARSER) {
2465 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
2466 Log.w(TAG, "Unknown element under <intent-filter>: " + parser.getName());
2467 XmlUtils.skipCurrentTag(parser);
2468 } else {
2469 outError[0] = "Bad element under <intent-filter>: " + parser.getName();
2470 return false;
2471 }
2472 }
2473
2474 outInfo.hasDefault = outInfo.hasCategory(Intent.CATEGORY_DEFAULT);
2475 if (false) {
2476 String cats = "";
2477 Iterator<String> it = outInfo.categoriesIterator();
2478 while (it != null && it.hasNext()) {
2479 cats += " " + it.next();
2480 }
2481 System.out.println("Intent d=" +
2482 outInfo.hasDefault + ", cat=" + cats);
2483 }
2484
2485 return true;
2486 }
2487
2488 public final static class Package {
2489 public final String packageName;
2490
2491 // For now we only support one application per package.
2492 public final ApplicationInfo applicationInfo = new ApplicationInfo();
2493
2494 public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
2495 public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
2496 public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
2497 public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
2498 public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
2499 public final ArrayList<Service> services = new ArrayList<Service>(0);
2500 public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);
2501
2502 public final ArrayList<String> requestedPermissions = new ArrayList<String>();
2503
Dianne Hackborn854060a2009-07-09 18:14:31 -07002504 public ArrayList<String> protectedBroadcasts;
2505
Dianne Hackborn49237342009-08-27 20:08:01 -07002506 public ArrayList<String> usesLibraries = null;
2507 public ArrayList<String> usesOptionalLibraries = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002508 public String[] usesLibraryFiles = null;
2509
2510 // We store the application meta-data independently to avoid multiple unwanted references
2511 public Bundle mAppMetaData = null;
2512
2513 // If this is a 3rd party app, this is the path of the zip file.
2514 public String mPath;
2515
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002516 // The version code declared for this package.
2517 public int mVersionCode;
2518
2519 // The version name declared for this package.
2520 public String mVersionName;
2521
2522 // The shared user id that this package wants to use.
2523 public String mSharedUserId;
2524
2525 // The shared user label that this package wants to use.
2526 public int mSharedUserLabel;
2527
2528 // Signatures that were read from the package.
2529 public Signature mSignatures[];
2530
2531 // For use by package manager service for quick lookup of
2532 // preferred up order.
2533 public int mPreferredOrder = 0;
2534
Dianne Hackborn5c1e00b2009-06-18 17:10:57 -07002535 // For use by package manager service to keep track of which apps
2536 // have been installed with forward locking.
2537 public boolean mForwardLocked;
2538
2539 // For use by the package manager to keep track of the path to the
2540 // file an app came from.
2541 public String mScanPath;
2542
2543 // For use by package manager to keep track of where it has done dexopt.
2544 public boolean mDidDexOpt;
2545
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002546 // Additional data supplied by callers.
2547 public Object mExtras;
2548
2549 /*
2550 * Applications hardware preferences
2551 */
2552 public final ArrayList<ConfigurationInfo> configPreferences =
2553 new ArrayList<ConfigurationInfo>();
2554
Dianne Hackborn49237342009-08-27 20:08:01 -07002555 /*
2556 * Applications requested features
2557 */
2558 public ArrayList<FeatureInfo> reqFeatures = null;
2559
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002560 public Package(String _name) {
2561 packageName = _name;
2562 applicationInfo.packageName = _name;
2563 applicationInfo.uid = -1;
2564 }
2565
2566 public String toString() {
2567 return "Package{"
2568 + Integer.toHexString(System.identityHashCode(this))
2569 + " " + packageName + "}";
2570 }
2571 }
2572
2573 public static class Component<II extends IntentInfo> {
2574 public final Package owner;
Dianne Hackborn1d442e02009-04-20 18:14:05 -07002575 public final ArrayList<II> intents;
2576 public final ComponentName component;
2577 public final String componentShortName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002578 public Bundle metaData;
2579
2580 public Component(Package _owner) {
2581 owner = _owner;
Dianne Hackborn1d442e02009-04-20 18:14:05 -07002582 intents = null;
2583 component = null;
2584 componentShortName = null;
2585 }
2586
2587 public Component(final ParsePackageItemArgs args, final PackageItemInfo outInfo) {
2588 owner = args.owner;
2589 intents = new ArrayList<II>(0);
2590 String name = args.sa.getNonResourceString(args.nameRes);
2591 if (name == null) {
2592 component = null;
2593 componentShortName = null;
2594 args.outError[0] = args.tag + " does not specify android:name";
2595 return;
2596 }
2597
2598 outInfo.name
2599 = buildClassName(owner.applicationInfo.packageName, name, args.outError);
2600 if (outInfo.name == null) {
2601 component = null;
2602 componentShortName = null;
2603 args.outError[0] = args.tag + " does not have valid android:name";
2604 return;
2605 }
2606
2607 component = new ComponentName(owner.applicationInfo.packageName,
2608 outInfo.name);
2609 componentShortName = component.flattenToShortString();
2610
2611 int iconVal = args.sa.getResourceId(args.iconRes, 0);
2612 if (iconVal != 0) {
2613 outInfo.icon = iconVal;
2614 outInfo.nonLocalizedLabel = null;
2615 }
2616
2617 TypedValue v = args.sa.peekValue(args.labelRes);
2618 if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
2619 outInfo.nonLocalizedLabel = v.coerceToString();
2620 }
2621
2622 outInfo.packageName = owner.packageName;
2623 }
2624
2625 public Component(final ParseComponentArgs args, final ComponentInfo outInfo) {
2626 this(args, (PackageItemInfo)outInfo);
2627 if (args.outError[0] != null) {
2628 return;
2629 }
2630
2631 if (args.processRes != 0) {
2632 outInfo.processName = buildProcessName(owner.applicationInfo.packageName,
2633 owner.applicationInfo.processName, args.sa.getNonResourceString(args.processRes),
2634 args.flags, args.sepProcesses, args.outError);
2635 }
2636 outInfo.enabled = args.sa.getBoolean(args.enabledRes, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002637 }
2638
2639 public Component(Component<II> clone) {
2640 owner = clone.owner;
Dianne Hackborn1d442e02009-04-20 18:14:05 -07002641 intents = clone.intents;
2642 component = clone.component;
2643 componentShortName = clone.componentShortName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002644 metaData = clone.metaData;
2645 }
2646 }
2647
2648 public final static class Permission extends Component<IntentInfo> {
2649 public final PermissionInfo info;
2650 public boolean tree;
2651 public PermissionGroup group;
2652
2653 public Permission(Package _owner) {
2654 super(_owner);
2655 info = new PermissionInfo();
2656 }
2657
2658 public Permission(Package _owner, PermissionInfo _info) {
2659 super(_owner);
2660 info = _info;
2661 }
2662
2663 public String toString() {
2664 return "Permission{"
2665 + Integer.toHexString(System.identityHashCode(this))
2666 + " " + info.name + "}";
2667 }
2668 }
2669
2670 public final static class PermissionGroup extends Component<IntentInfo> {
2671 public final PermissionGroupInfo info;
2672
2673 public PermissionGroup(Package _owner) {
2674 super(_owner);
2675 info = new PermissionGroupInfo();
2676 }
2677
2678 public PermissionGroup(Package _owner, PermissionGroupInfo _info) {
2679 super(_owner);
2680 info = _info;
2681 }
2682
2683 public String toString() {
2684 return "PermissionGroup{"
2685 + Integer.toHexString(System.identityHashCode(this))
2686 + " " + info.name + "}";
2687 }
2688 }
2689
2690 private static boolean copyNeeded(int flags, Package p, Bundle metaData) {
2691 if ((flags & PackageManager.GET_META_DATA) != 0
2692 && (metaData != null || p.mAppMetaData != null)) {
2693 return true;
2694 }
2695 if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0
2696 && p.usesLibraryFiles != null) {
2697 return true;
2698 }
2699 return false;
2700 }
2701
2702 public static ApplicationInfo generateApplicationInfo(Package p, int flags) {
2703 if (p == null) return null;
2704 if (!copyNeeded(flags, p, null)) {
Mitsuru Oshima69fff4a2009-07-21 09:51:05 -07002705 // CompatibilityMode is global state. It's safe to modify the instance
2706 // of the package.
2707 if (!sCompatibilityModeEnabled) {
2708 p.applicationInfo.disableCompatibilityMode();
2709 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002710 return p.applicationInfo;
2711 }
2712
2713 // Make shallow copy so we can store the metadata/libraries safely
2714 ApplicationInfo ai = new ApplicationInfo(p.applicationInfo);
2715 if ((flags & PackageManager.GET_META_DATA) != 0) {
2716 ai.metaData = p.mAppMetaData;
2717 }
2718 if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0) {
2719 ai.sharedLibraryFiles = p.usesLibraryFiles;
2720 }
Mitsuru Oshima69fff4a2009-07-21 09:51:05 -07002721 if (!sCompatibilityModeEnabled) {
2722 ai.disableCompatibilityMode();
2723 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002724 return ai;
2725 }
2726
2727 public static final PermissionInfo generatePermissionInfo(
2728 Permission p, int flags) {
2729 if (p == null) return null;
2730 if ((flags&PackageManager.GET_META_DATA) == 0) {
2731 return p.info;
2732 }
2733 PermissionInfo pi = new PermissionInfo(p.info);
2734 pi.metaData = p.metaData;
2735 return pi;
2736 }
2737
2738 public static final PermissionGroupInfo generatePermissionGroupInfo(
2739 PermissionGroup pg, int flags) {
2740 if (pg == null) return null;
2741 if ((flags&PackageManager.GET_META_DATA) == 0) {
2742 return pg.info;
2743 }
2744 PermissionGroupInfo pgi = new PermissionGroupInfo(pg.info);
2745 pgi.metaData = pg.metaData;
2746 return pgi;
2747 }
2748
2749 public final static class Activity extends Component<ActivityIntentInfo> {
Dianne Hackborn1d442e02009-04-20 18:14:05 -07002750 public final ActivityInfo info;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002751
Dianne Hackborn1d442e02009-04-20 18:14:05 -07002752 public Activity(final ParseComponentArgs args, final ActivityInfo _info) {
2753 super(args, _info);
2754 info = _info;
2755 info.applicationInfo = args.owner.applicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002756 }
Dianne Hackborn1d442e02009-04-20 18:14:05 -07002757
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002758 public String toString() {
2759 return "Activity{"
2760 + Integer.toHexString(System.identityHashCode(this))
2761 + " " + component.flattenToString() + "}";
2762 }
2763 }
2764
2765 public static final ActivityInfo generateActivityInfo(Activity a,
2766 int flags) {
2767 if (a == null) return null;
2768 if (!copyNeeded(flags, a.owner, a.metaData)) {
2769 return a.info;
2770 }
2771 // Make shallow copies so we can store the metadata safely
2772 ActivityInfo ai = new ActivityInfo(a.info);
2773 ai.metaData = a.metaData;
2774 ai.applicationInfo = generateApplicationInfo(a.owner, flags);
2775 return ai;
2776 }
2777
2778 public final static class Service extends Component<ServiceIntentInfo> {
Dianne Hackborn1d442e02009-04-20 18:14:05 -07002779 public final ServiceInfo info;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002780
Dianne Hackborn1d442e02009-04-20 18:14:05 -07002781 public Service(final ParseComponentArgs args, final ServiceInfo _info) {
2782 super(args, _info);
2783 info = _info;
2784 info.applicationInfo = args.owner.applicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002785 }
Dianne Hackborn1d442e02009-04-20 18:14:05 -07002786
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002787 public String toString() {
2788 return "Service{"
2789 + Integer.toHexString(System.identityHashCode(this))
2790 + " " + component.flattenToString() + "}";
2791 }
2792 }
2793
2794 public static final ServiceInfo generateServiceInfo(Service s, int flags) {
2795 if (s == null) return null;
2796 if (!copyNeeded(flags, s.owner, s.metaData)) {
2797 return s.info;
2798 }
2799 // Make shallow copies so we can store the metadata safely
2800 ServiceInfo si = new ServiceInfo(s.info);
2801 si.metaData = s.metaData;
2802 si.applicationInfo = generateApplicationInfo(s.owner, flags);
2803 return si;
2804 }
2805
2806 public final static class Provider extends Component {
2807 public final ProviderInfo info;
2808 public boolean syncable;
2809
Dianne Hackborn1d442e02009-04-20 18:14:05 -07002810 public Provider(final ParseComponentArgs args, final ProviderInfo _info) {
2811 super(args, _info);
2812 info = _info;
2813 info.applicationInfo = args.owner.applicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002814 syncable = false;
2815 }
Dianne Hackborn1d442e02009-04-20 18:14:05 -07002816
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002817 public Provider(Provider existingProvider) {
2818 super(existingProvider);
2819 this.info = existingProvider.info;
2820 this.syncable = existingProvider.syncable;
2821 }
2822
2823 public String toString() {
2824 return "Provider{"
2825 + Integer.toHexString(System.identityHashCode(this))
2826 + " " + info.name + "}";
2827 }
2828 }
2829
2830 public static final ProviderInfo generateProviderInfo(Provider p,
2831 int flags) {
2832 if (p == null) return null;
2833 if (!copyNeeded(flags, p.owner, p.metaData)
2834 && ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0
2835 || p.info.uriPermissionPatterns == null)) {
2836 return p.info;
2837 }
2838 // Make shallow copies so we can store the metadata safely
2839 ProviderInfo pi = new ProviderInfo(p.info);
2840 pi.metaData = p.metaData;
2841 if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
2842 pi.uriPermissionPatterns = null;
2843 }
2844 pi.applicationInfo = generateApplicationInfo(p.owner, flags);
2845 return pi;
2846 }
2847
2848 public final static class Instrumentation extends Component {
Dianne Hackborn1d442e02009-04-20 18:14:05 -07002849 public final InstrumentationInfo info;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002850
Dianne Hackborn1d442e02009-04-20 18:14:05 -07002851 public Instrumentation(final ParsePackageItemArgs args, final InstrumentationInfo _info) {
2852 super(args, _info);
2853 info = _info;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002854 }
Dianne Hackborn1d442e02009-04-20 18:14:05 -07002855
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002856 public String toString() {
2857 return "Instrumentation{"
2858 + Integer.toHexString(System.identityHashCode(this))
2859 + " " + component.flattenToString() + "}";
2860 }
2861 }
2862
2863 public static final InstrumentationInfo generateInstrumentationInfo(
2864 Instrumentation i, int flags) {
2865 if (i == null) return null;
2866 if ((flags&PackageManager.GET_META_DATA) == 0) {
2867 return i.info;
2868 }
2869 InstrumentationInfo ii = new InstrumentationInfo(i.info);
2870 ii.metaData = i.metaData;
2871 return ii;
2872 }
2873
2874 public static class IntentInfo extends IntentFilter {
2875 public boolean hasDefault;
2876 public int labelRes;
2877 public CharSequence nonLocalizedLabel;
2878 public int icon;
2879 }
2880
2881 public final static class ActivityIntentInfo extends IntentInfo {
2882 public final Activity activity;
2883
2884 public ActivityIntentInfo(Activity _activity) {
2885 activity = _activity;
2886 }
2887
2888 public String toString() {
2889 return "ActivityIntentInfo{"
2890 + Integer.toHexString(System.identityHashCode(this))
2891 + " " + activity.info.name + "}";
2892 }
2893 }
2894
2895 public final static class ServiceIntentInfo extends IntentInfo {
2896 public final Service service;
2897
2898 public ServiceIntentInfo(Service _service) {
2899 service = _service;
2900 }
2901
2902 public String toString() {
2903 return "ServiceIntentInfo{"
2904 + Integer.toHexString(System.identityHashCode(this))
2905 + " " + service.info.name + "}";
2906 }
2907 }
Mitsuru Oshima69fff4a2009-07-21 09:51:05 -07002908
2909 /**
2910 * @hide
2911 */
2912 public static void setCompatibilityModeEnabled(boolean compatibilityModeEnabled) {
2913 sCompatibilityModeEnabled = compatibilityModeEnabled;
2914 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002915}