PackageParser.java revision 45515659438964ec47f4feac247f0e9dce587c86
1/*
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;
48import java.util.List;
49import java.util.jar.JarEntry;
50import java.util.jar.JarFile;
51
52/**
53 * Package archive parsing
54 *
55 * {@hide}
56 */
57public class PackageParser {
58    /** @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     */
77    public static final PackageParser.NewPermissionInfo NEW_PERMISSIONS[] =
78        new PackageParser.NewPermissionInfo[] {
79            new PackageParser.NewPermissionInfo(android.Manifest.permission.WRITE_SDCARD,
80                    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)
83    };
84
85    private String mArchiveSourcePath;
86    private String[] mSeparateProcesses;
87    private int mSdkVersion;
88    private String mSdkCodename;
89
90    private int mParseError = PackageManager.INSTALL_SUCCEEDED;
91
92    private static final Object mSync = new Object();
93    private static WeakReference<byte[]> mReadBuffer;
94
95    static class ParsePackageItemArgs {
96        final Package owner;
97        final String[] outError;
98        final int nameRes;
99        final int labelRes;
100        final int iconRes;
101
102        String tag;
103        TypedArray sa;
104
105        ParsePackageItemArgs(Package _owner, String[] _outError,
106                int _nameRes, int _labelRes, int _iconRes) {
107            owner = _owner;
108            outError = _outError;
109            nameRes = _nameRes;
110            labelRes = _labelRes;
111            iconRes = _iconRes;
112        }
113    }
114
115    static class ParseComponentArgs extends ParsePackageItemArgs {
116        final String[] sepProcesses;
117        final int processRes;
118        final int enabledRes;
119        int flags;
120
121        ParseComponentArgs(Package _owner, String[] _outError,
122                int _nameRes, int _labelRes, int _iconRes,
123                String[] _sepProcesses, int _processRes,int _enabledRes) {
124            super(_owner, _outError, _nameRes, _labelRes, _iconRes);
125            sepProcesses = _sepProcesses;
126            processRes = _processRes;
127            enabledRes = _enabledRes;
128        }
129    }
130
131    private ParsePackageItemArgs mParseInstrumentationArgs;
132    private ParseComponentArgs mParseActivityArgs;
133    private ParseComponentArgs mParseActivityAliasArgs;
134    private ParseComponentArgs mParseServiceArgs;
135    private ParseComponentArgs mParseProviderArgs;
136
137    /** If set to true, we will only allow package files that exactly match
138     *  the DTD.  Otherwise, we try to get as much from the package as we
139     *  can without failing.  This should normally be set to false, to
140     *  support extensions to the DTD in future versions. */
141    private static final boolean RIGID_PARSER = false;
142
143    private static final String TAG = "PackageParser";
144
145    public PackageParser(String archiveSourcePath) {
146        mArchiveSourcePath = archiveSourcePath;
147    }
148
149    public void setSeparateProcesses(String[] procs) {
150        mSeparateProcesses = procs;
151    }
152
153    public void setSdkVersion(int sdkVersion, String codename) {
154        mSdkVersion = sdkVersion;
155        mSdkCodename = codename;
156    }
157
158    private static final boolean isPackageFilename(String name) {
159        return name.endsWith(".apk");
160    }
161
162    /**
163     * Generate and return the {@link PackageInfo} for a parsed package.
164     *
165     * @param p the parsed package.
166     * @param flags indicating which optional information is included.
167     */
168    public static PackageInfo generatePackageInfo(PackageParser.Package p,
169            int gids[], int flags) {
170
171        PackageInfo pi = new PackageInfo();
172        pi.packageName = p.packageName;
173        pi.versionCode = p.mVersionCode;
174        pi.versionName = p.mVersionName;
175        pi.sharedUserId = p.mSharedUserId;
176        pi.sharedUserLabel = p.mSharedUserLabel;
177        pi.applicationInfo = p.applicationInfo;
178        if ((flags&PackageManager.GET_GIDS) != 0) {
179            pi.gids = gids;
180        }
181        if ((flags&PackageManager.GET_CONFIGURATIONS) != 0) {
182            int N = p.configPreferences.size();
183            if (N > 0) {
184                pi.configPreferences = new ConfigurationInfo[N];
185                for (int i=0; i<N; i++) {
186                    pi.configPreferences[i] = p.configPreferences.get(i);
187                }
188            }
189        }
190        if ((flags&PackageManager.GET_ACTIVITIES) != 0) {
191            int N = p.activities.size();
192            if (N > 0) {
193                pi.activities = new ActivityInfo[N];
194                for (int i=0; i<N; i++) {
195                    final Activity activity = p.activities.get(i);
196                    if (activity.info.enabled
197                        || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
198                        pi.activities[i] = generateActivityInfo(p.activities.get(i), flags);
199                    }
200                }
201            }
202        }
203        if ((flags&PackageManager.GET_RECEIVERS) != 0) {
204            int N = p.receivers.size();
205            if (N > 0) {
206                pi.receivers = new ActivityInfo[N];
207                for (int i=0; i<N; i++) {
208                    final Activity activity = p.receivers.get(i);
209                    if (activity.info.enabled
210                        || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
211                        pi.receivers[i] = generateActivityInfo(p.receivers.get(i), flags);
212                    }
213                }
214            }
215        }
216        if ((flags&PackageManager.GET_SERVICES) != 0) {
217            int N = p.services.size();
218            if (N > 0) {
219                pi.services = new ServiceInfo[N];
220                for (int i=0; i<N; i++) {
221                    final Service service = p.services.get(i);
222                    if (service.info.enabled
223                        || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
224                        pi.services[i] = generateServiceInfo(p.services.get(i), flags);
225                    }
226                }
227            }
228        }
229        if ((flags&PackageManager.GET_PROVIDERS) != 0) {
230            int N = p.providers.size();
231            if (N > 0) {
232                pi.providers = new ProviderInfo[N];
233                for (int i=0; i<N; i++) {
234                    final Provider provider = p.providers.get(i);
235                    if (provider.info.enabled
236                        || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
237                        pi.providers[i] = generateProviderInfo(p.providers.get(i), flags);
238                    }
239                }
240            }
241        }
242        if ((flags&PackageManager.GET_INSTRUMENTATION) != 0) {
243            int N = p.instrumentation.size();
244            if (N > 0) {
245                pi.instrumentation = new InstrumentationInfo[N];
246                for (int i=0; i<N; i++) {
247                    pi.instrumentation[i] = generateInstrumentationInfo(
248                            p.instrumentation.get(i), flags);
249                }
250            }
251        }
252        if ((flags&PackageManager.GET_PERMISSIONS) != 0) {
253            int N = p.permissions.size();
254            if (N > 0) {
255                pi.permissions = new PermissionInfo[N];
256                for (int i=0; i<N; i++) {
257                    pi.permissions[i] = generatePermissionInfo(p.permissions.get(i), flags);
258                }
259            }
260            N = p.requestedPermissions.size();
261            if (N > 0) {
262                pi.requestedPermissions = new String[N];
263                for (int i=0; i<N; i++) {
264                    pi.requestedPermissions[i] = p.requestedPermissions.get(i);
265                }
266            }
267        }
268        if ((flags&PackageManager.GET_SIGNATURES) != 0) {
269            int N = p.mSignatures.length;
270            if (N > 0) {
271                pi.signatures = new Signature[N];
272                System.arraycopy(p.mSignatures, 0, pi.signatures, 0, N);
273            }
274        }
275        return pi;
276    }
277
278    private Certificate[] loadCertificates(JarFile jarFile, JarEntry je,
279            byte[] readBuffer) {
280        try {
281            // We must read the stream for the JarEntry to retrieve
282            // its certificates.
283            InputStream is = jarFile.getInputStream(je);
284            while (is.read(readBuffer, 0, readBuffer.length) != -1) {
285                // not using
286            }
287            is.close();
288            return je != null ? je.getCertificates() : null;
289        } catch (IOException e) {
290            Log.w(TAG, "Exception reading " + je.getName() + " in "
291                    + jarFile.getName(), e);
292        }
293        return null;
294    }
295
296    public final static int PARSE_IS_SYSTEM = 0x0001;
297    public final static int PARSE_CHATTY = 0x0002;
298    public final static int PARSE_MUST_BE_APK = 0x0004;
299    public final static int PARSE_IGNORE_PROCESSES = 0x0008;
300
301    public int getParseError() {
302        return mParseError;
303    }
304
305    public Package parsePackage(File sourceFile, String destFileName,
306            DisplayMetrics metrics, int flags) {
307        mParseError = PackageManager.INSTALL_SUCCEEDED;
308
309        mArchiveSourcePath = sourceFile.getPath();
310        if (!sourceFile.isFile()) {
311            Log.w(TAG, "Skipping dir: " + mArchiveSourcePath);
312            mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
313            return null;
314        }
315        if (!isPackageFilename(sourceFile.getName())
316                && (flags&PARSE_MUST_BE_APK) != 0) {
317            if ((flags&PARSE_IS_SYSTEM) == 0) {
318                // We expect to have non-.apk files in the system dir,
319                // so don't warn about them.
320                Log.w(TAG, "Skipping non-package file: " + mArchiveSourcePath);
321            }
322            mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
323            return null;
324        }
325
326        if ((flags&PARSE_CHATTY) != 0 && Config.LOGD) Log.d(
327            TAG, "Scanning package: " + mArchiveSourcePath);
328
329        XmlResourceParser parser = null;
330        AssetManager assmgr = null;
331        boolean assetError = true;
332        try {
333            assmgr = new AssetManager();
334            int cookie = assmgr.addAssetPath(mArchiveSourcePath);
335            if(cookie != 0) {
336                parser = assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml");
337                assetError = false;
338            } else {
339                Log.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);
340            }
341        } catch (Exception e) {
342            Log.w(TAG, "Unable to read AndroidManifest.xml of "
343                    + mArchiveSourcePath, e);
344        }
345        if(assetError) {
346            if (assmgr != null) assmgr.close();
347            mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
348            return null;
349        }
350        String[] errorText = new String[1];
351        Package pkg = null;
352        Exception errorException = null;
353        try {
354            // XXXX todo: need to figure out correct configuration.
355            Resources res = new Resources(assmgr, metrics, null);
356            pkg = parsePackage(res, parser, flags, errorText);
357        } catch (Exception e) {
358            errorException = e;
359            mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
360        }
361
362
363        if (pkg == null) {
364            if (errorException != null) {
365                Log.w(TAG, mArchiveSourcePath, errorException);
366            } else {
367                Log.w(TAG, mArchiveSourcePath + " (at "
368                        + parser.getPositionDescription()
369                        + "): " + errorText[0]);
370            }
371            parser.close();
372            assmgr.close();
373            if (mParseError == PackageManager.INSTALL_SUCCEEDED) {
374                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
375            }
376            return null;
377        }
378
379        parser.close();
380        assmgr.close();
381
382        pkg.applicationInfo.sourceDir = destFileName;
383        pkg.applicationInfo.publicSourceDir = destFileName;
384        pkg.mSignatures = null;
385
386        return pkg;
387    }
388
389    public boolean collectCertificates(Package pkg, int flags) {
390        pkg.mSignatures = null;
391
392        WeakReference<byte[]> readBufferRef;
393        byte[] readBuffer = null;
394        synchronized (mSync) {
395            readBufferRef = mReadBuffer;
396            if (readBufferRef != null) {
397                mReadBuffer = null;
398                readBuffer = readBufferRef.get();
399            }
400            if (readBuffer == null) {
401                readBuffer = new byte[8192];
402                readBufferRef = new WeakReference<byte[]>(readBuffer);
403            }
404        }
405
406        try {
407            JarFile jarFile = new JarFile(mArchiveSourcePath);
408
409            Certificate[] certs = null;
410
411            if ((flags&PARSE_IS_SYSTEM) != 0) {
412                // If this package comes from the system image, then we
413                // can trust it...  we'll just use the AndroidManifest.xml
414                // to retrieve its signatures, not validating all of the
415                // files.
416                JarEntry jarEntry = jarFile.getJarEntry("AndroidManifest.xml");
417                certs = loadCertificates(jarFile, jarEntry, readBuffer);
418                if (certs == null) {
419                    Log.e(TAG, "Package " + pkg.packageName
420                            + " has no certificates at entry "
421                            + jarEntry.getName() + "; ignoring!");
422                    jarFile.close();
423                    mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
424                    return false;
425                }
426                if (false) {
427                    Log.i(TAG, "File " + mArchiveSourcePath + ": entry=" + jarEntry
428                            + " certs=" + (certs != null ? certs.length : 0));
429                    if (certs != null) {
430                        final int N = certs.length;
431                        for (int i=0; i<N; i++) {
432                            Log.i(TAG, "  Public key: "
433                                    + certs[i].getPublicKey().getEncoded()
434                                    + " " + certs[i].getPublicKey());
435                        }
436                    }
437                }
438
439            } else {
440                Enumeration entries = jarFile.entries();
441                while (entries.hasMoreElements()) {
442                    JarEntry je = (JarEntry)entries.nextElement();
443                    if (je.isDirectory()) continue;
444                    if (je.getName().startsWith("META-INF/")) continue;
445                    Certificate[] localCerts = loadCertificates(jarFile, je,
446                            readBuffer);
447                    if (false) {
448                        Log.i(TAG, "File " + mArchiveSourcePath + " entry " + je.getName()
449                                + ": certs=" + certs + " ("
450                                + (certs != null ? certs.length : 0) + ")");
451                    }
452                    if (localCerts == null) {
453                        Log.e(TAG, "Package " + pkg.packageName
454                                + " has no certificates at entry "
455                                + je.getName() + "; ignoring!");
456                        jarFile.close();
457                        mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
458                        return false;
459                    } else if (certs == null) {
460                        certs = localCerts;
461                    } else {
462                        // Ensure all certificates match.
463                        for (int i=0; i<certs.length; i++) {
464                            boolean found = false;
465                            for (int j=0; j<localCerts.length; j++) {
466                                if (certs[i] != null &&
467                                        certs[i].equals(localCerts[j])) {
468                                    found = true;
469                                    break;
470                                }
471                            }
472                            if (!found || certs.length != localCerts.length) {
473                                Log.e(TAG, "Package " + pkg.packageName
474                                        + " has mismatched certificates at entry "
475                                        + je.getName() + "; ignoring!");
476                                jarFile.close();
477                                mParseError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
478                                return false;
479                            }
480                        }
481                    }
482                }
483            }
484            jarFile.close();
485
486            synchronized (mSync) {
487                mReadBuffer = readBufferRef;
488            }
489
490            if (certs != null && certs.length > 0) {
491                final int N = certs.length;
492                pkg.mSignatures = new Signature[certs.length];
493                for (int i=0; i<N; i++) {
494                    pkg.mSignatures[i] = new Signature(
495                            certs[i].getEncoded());
496                }
497            } else {
498                Log.e(TAG, "Package " + pkg.packageName
499                        + " has no certificates; ignoring!");
500                mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
501                return false;
502            }
503        } catch (CertificateEncodingException e) {
504            Log.w(TAG, "Exception reading " + mArchiveSourcePath, e);
505            mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
506            return false;
507        } catch (IOException e) {
508            Log.w(TAG, "Exception reading " + mArchiveSourcePath, e);
509            mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
510            return false;
511        } catch (RuntimeException e) {
512            Log.w(TAG, "Exception reading " + mArchiveSourcePath, e);
513            mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
514            return false;
515        }
516
517        return true;
518    }
519
520    public static String parsePackageName(String packageFilePath, int flags) {
521        XmlResourceParser parser = null;
522        AssetManager assmgr = null;
523        try {
524            assmgr = new AssetManager();
525            int cookie = assmgr.addAssetPath(packageFilePath);
526            parser = assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml");
527        } catch (Exception e) {
528            if (assmgr != null) assmgr.close();
529            Log.w(TAG, "Unable to read AndroidManifest.xml of "
530                    + packageFilePath, e);
531            return null;
532        }
533        AttributeSet attrs = parser;
534        String errors[] = new String[1];
535        String packageName = null;
536        try {
537            packageName = parsePackageName(parser, attrs, flags, errors);
538        } catch (IOException e) {
539            Log.w(TAG, packageFilePath, e);
540        } catch (XmlPullParserException e) {
541            Log.w(TAG, packageFilePath, e);
542        } finally {
543            if (parser != null) parser.close();
544            if (assmgr != null) assmgr.close();
545        }
546        if (packageName == null) {
547            Log.e(TAG, "parsePackageName error: " + errors[0]);
548            return null;
549        }
550        return packageName;
551    }
552
553    private static String validateName(String name, boolean requiresSeparator) {
554        final int N = name.length();
555        boolean hasSep = false;
556        boolean front = true;
557        for (int i=0; i<N; i++) {
558            final char c = name.charAt(i);
559            if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
560                front = false;
561                continue;
562            }
563            if (!front) {
564                if ((c >= '0' && c <= '9') || c == '_') {
565                    continue;
566                }
567            }
568            if (c == '.') {
569                hasSep = true;
570                front = true;
571                continue;
572            }
573            return "bad character '" + c + "'";
574        }
575        return hasSep || !requiresSeparator
576                ? null : "must have at least one '.' separator";
577    }
578
579    private static String parsePackageName(XmlPullParser parser,
580            AttributeSet attrs, int flags, String[] outError)
581            throws IOException, XmlPullParserException {
582
583        int type;
584        while ((type=parser.next()) != parser.START_TAG
585                   && type != parser.END_DOCUMENT) {
586            ;
587        }
588
589        if (type != parser.START_TAG) {
590            outError[0] = "No start tag found";
591            return null;
592        }
593        if ((flags&PARSE_CHATTY) != 0 && Config.LOGV) Log.v(
594            TAG, "Root element name: '" + parser.getName() + "'");
595        if (!parser.getName().equals("manifest")) {
596            outError[0] = "No <manifest> tag";
597            return null;
598        }
599        String pkgName = attrs.getAttributeValue(null, "package");
600        if (pkgName == null || pkgName.length() == 0) {
601            outError[0] = "<manifest> does not specify package";
602            return null;
603        }
604        String nameError = validateName(pkgName, true);
605        if (nameError != null && !"android".equals(pkgName)) {
606            outError[0] = "<manifest> specifies bad package name \""
607                + pkgName + "\": " + nameError;
608            return null;
609        }
610
611        return pkgName.intern();
612    }
613
614    /**
615     * Temporary.
616     */
617    static public Signature stringToSignature(String str) {
618        final int N = str.length();
619        byte[] sig = new byte[N];
620        for (int i=0; i<N; i++) {
621            sig[i] = (byte)str.charAt(i);
622        }
623        return new Signature(sig);
624    }
625
626    private Package parsePackage(
627        Resources res, XmlResourceParser parser, int flags, String[] outError)
628        throws XmlPullParserException, IOException {
629        AttributeSet attrs = parser;
630
631        mParseInstrumentationArgs = null;
632        mParseActivityArgs = null;
633        mParseServiceArgs = null;
634        mParseProviderArgs = null;
635
636        String pkgName = parsePackageName(parser, attrs, flags, outError);
637        if (pkgName == null) {
638            mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
639            return null;
640        }
641        int type;
642
643        final Package pkg = new Package(pkgName);
644        boolean foundApp = false;
645
646        TypedArray sa = res.obtainAttributes(attrs,
647                com.android.internal.R.styleable.AndroidManifest);
648        pkg.mVersionCode = sa.getInteger(
649                com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
650        pkg.mVersionName = sa.getNonResourceString(
651                com.android.internal.R.styleable.AndroidManifest_versionName);
652        if (pkg.mVersionName != null) {
653            pkg.mVersionName = pkg.mVersionName.intern();
654        }
655        String str = sa.getNonResourceString(
656                com.android.internal.R.styleable.AndroidManifest_sharedUserId);
657        if (str != null) {
658            String nameError = validateName(str, true);
659            if (nameError != null && !"android".equals(pkgName)) {
660                outError[0] = "<manifest> specifies bad sharedUserId name \""
661                    + str + "\": " + nameError;
662                mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
663                return null;
664            }
665            pkg.mSharedUserId = str.intern();
666            pkg.mSharedUserLabel = sa.getResourceId(
667                    com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
668        }
669        sa.recycle();
670
671        int outerDepth = parser.getDepth();
672        while ((type=parser.next()) != parser.END_DOCUMENT
673               && (type != parser.END_TAG || parser.getDepth() > outerDepth)) {
674            if (type == parser.END_TAG || type == parser.TEXT) {
675                continue;
676            }
677
678            String tagName = parser.getName();
679            if (tagName.equals("application")) {
680                if (foundApp) {
681                    if (RIGID_PARSER) {
682                        outError[0] = "<manifest> has more than one <application>";
683                        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
684                        return null;
685                    } else {
686                        Log.w(TAG, "<manifest> has more than one <application>");
687                        XmlUtils.skipCurrentTag(parser);
688                        continue;
689                    }
690                }
691
692                foundApp = true;
693                if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {
694                    return null;
695                }
696            } else if (tagName.equals("permission-group")) {
697                if (parsePermissionGroup(pkg, res, parser, attrs, outError) == null) {
698                    return null;
699                }
700            } else if (tagName.equals("permission")) {
701                if (parsePermission(pkg, res, parser, attrs, outError) == null) {
702                    return null;
703                }
704            } else if (tagName.equals("permission-tree")) {
705                if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
706                    return null;
707                }
708            } else if (tagName.equals("uses-permission")) {
709                sa = res.obtainAttributes(attrs,
710                        com.android.internal.R.styleable.AndroidManifestUsesPermission);
711
712                String name = sa.getNonResourceString(
713                        com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
714
715                sa.recycle();
716
717                if (name != null && !pkg.requestedPermissions.contains(name)) {
718                    pkg.requestedPermissions.add(name);
719                }
720
721                XmlUtils.skipCurrentTag(parser);
722
723            } else if (tagName.equals("uses-configuration")) {
724                ConfigurationInfo cPref = new ConfigurationInfo();
725                sa = res.obtainAttributes(attrs,
726                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration);
727                cPref.reqTouchScreen = sa.getInt(
728                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
729                        Configuration.TOUCHSCREEN_UNDEFINED);
730                cPref.reqKeyboardType = sa.getInt(
731                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
732                        Configuration.KEYBOARD_UNDEFINED);
733                if (sa.getBoolean(
734                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
735                        false)) {
736                    cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
737                }
738                cPref.reqNavigation = sa.getInt(
739                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
740                        Configuration.NAVIGATION_UNDEFINED);
741                if (sa.getBoolean(
742                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
743                        false)) {
744                    cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
745                }
746                sa.recycle();
747                pkg.configPreferences.add(cPref);
748
749                XmlUtils.skipCurrentTag(parser);
750
751            } else if (tagName.equals("uses-sdk")) {
752                if (mSdkVersion > 0) {
753                    sa = res.obtainAttributes(attrs,
754                            com.android.internal.R.styleable.AndroidManifestUsesSdk);
755
756                    int minVers = 0;
757                    String minCode = null;
758                    int targetVers = 0;
759                    String targetCode = null;
760
761                    TypedValue val = sa.peekValue(
762                            com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
763                    if (val != null) {
764                        if (val.type == TypedValue.TYPE_STRING && val.string != null) {
765                            targetCode = minCode = val.string.toString();
766                        } else {
767                            // If it's not a string, it's an integer.
768                            minVers = val.data;
769                        }
770                    }
771
772                    val = sa.peekValue(
773                            com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
774                    if (val != null) {
775                        if (val.type == TypedValue.TYPE_STRING && val.string != null) {
776                            targetCode = minCode = val.string.toString();
777                        } else {
778                            // If it's not a string, it's an integer.
779                            targetVers = val.data;
780                        }
781                    }
782
783                    int maxVers = sa.getInt(
784                            com.android.internal.R.styleable.AndroidManifestUsesSdk_maxSdkVersion,
785                            mSdkVersion);
786
787                    sa.recycle();
788
789                    if (targetCode != null) {
790                        if (!targetCode.equals(mSdkCodename)) {
791                            if (mSdkCodename != null) {
792                                outError[0] = "Requires development platform " + targetCode
793                                        + " (current platform is " + mSdkCodename + ")";
794                            } else {
795                                outError[0] = "Requires development platform " + targetCode
796                                        + " but this is a release platform.";
797                            }
798                            mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
799                            return null;
800                        }
801                        // If the code matches, it definitely targets this SDK.
802                        pkg.applicationInfo.targetSdkVersion
803                                = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
804                    } else {
805                        pkg.applicationInfo.targetSdkVersion = targetVers;
806                    }
807
808                    if (minVers > mSdkVersion) {
809                        outError[0] = "Requires newer sdk version #" + minVers
810                                + " (current version is #" + mSdkVersion + ")";
811                        mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
812                        return null;
813                    }
814
815                    if (maxVers < mSdkVersion) {
816                        outError[0] = "Requires older sdk version #" + maxVers
817                                + " (current version is #" + mSdkVersion + ")";
818                        mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
819                        return null;
820                    }
821                }
822
823                XmlUtils.skipCurrentTag(parser);
824
825            } else if (tagName.equals("instrumentation")) {
826                if (parseInstrumentation(pkg, res, parser, attrs, outError) == null) {
827                    return null;
828                }
829            } else if (tagName.equals("eat-comment")) {
830                // Just skip this tag
831                XmlUtils.skipCurrentTag(parser);
832                continue;
833            } else if (RIGID_PARSER) {
834                outError[0] = "Bad element under <manifest>: "
835                    + parser.getName();
836                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
837                return null;
838            } else {
839                Log.w(TAG, "Bad element under <manifest>: "
840                      + parser.getName());
841                XmlUtils.skipCurrentTag(parser);
842                continue;
843            }
844        }
845
846        if (!foundApp && pkg.instrumentation.size() == 0) {
847            outError[0] = "<manifest> does not contain an <application> or <instrumentation>";
848            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY;
849        }
850
851        final int NP = PackageParser.NEW_PERMISSIONS.length;
852        for (int ip=0; ip<NP; ip++) {
853            final PackageParser.NewPermissionInfo npi
854                    = PackageParser.NEW_PERMISSIONS[ip];
855            if (pkg.applicationInfo.targetSdkVersion >= npi.sdkVersion) {
856                break;
857            }
858            if (!pkg.requestedPermissions.contains(npi.name)) {
859                Log.i(TAG, "Impliciting adding " + npi.name + " to old pkg "
860                        + pkg.packageName);
861                pkg.requestedPermissions.add(npi.name);
862            }
863        }
864
865        if (pkg.usesLibraries.size() > 0) {
866            pkg.usesLibraryFiles = new String[pkg.usesLibraries.size()];
867            pkg.usesLibraries.toArray(pkg.usesLibraryFiles);
868        }
869
870        int size = pkg.supportsDensityList.size();
871        if (size > 0) {
872            int densities[] = pkg.supportsDensities = new int[size];
873            List<Integer> densityList = pkg.supportsDensityList;
874            for (int i = 0; i < size; i++) {
875                densities[i] = densityList.get(i);
876            }
877        }
878        return pkg;
879    }
880
881    private static String buildClassName(String pkg, CharSequence clsSeq,
882            String[] outError) {
883        if (clsSeq == null || clsSeq.length() <= 0) {
884            outError[0] = "Empty class name in package " + pkg;
885            return null;
886        }
887        String cls = clsSeq.toString();
888        char c = cls.charAt(0);
889        if (c == '.') {
890            return (pkg + cls).intern();
891        }
892        if (cls.indexOf('.') < 0) {
893            StringBuilder b = new StringBuilder(pkg);
894            b.append('.');
895            b.append(cls);
896            return b.toString().intern();
897        }
898        if (c >= 'a' && c <= 'z') {
899            return cls.intern();
900        }
901        outError[0] = "Bad class name " + cls + " in package " + pkg;
902        return null;
903    }
904
905    private static String buildCompoundName(String pkg,
906            CharSequence procSeq, String type, String[] outError) {
907        String proc = procSeq.toString();
908        char c = proc.charAt(0);
909        if (pkg != null && c == ':') {
910            if (proc.length() < 2) {
911                outError[0] = "Bad " + type + " name " + proc + " in package " + pkg
912                        + ": must be at least two characters";
913                return null;
914            }
915            String subName = proc.substring(1);
916            String nameError = validateName(subName, false);
917            if (nameError != null) {
918                outError[0] = "Invalid " + type + " name " + proc + " in package "
919                        + pkg + ": " + nameError;
920                return null;
921            }
922            return (pkg + proc).intern();
923        }
924        String nameError = validateName(proc, true);
925        if (nameError != null && !"system".equals(proc)) {
926            outError[0] = "Invalid " + type + " name " + proc + " in package "
927                    + pkg + ": " + nameError;
928            return null;
929        }
930        return proc.intern();
931    }
932
933    private static String buildProcessName(String pkg, String defProc,
934            CharSequence procSeq, int flags, String[] separateProcesses,
935            String[] outError) {
936        if ((flags&PARSE_IGNORE_PROCESSES) != 0 && !"system".equals(procSeq)) {
937            return defProc != null ? defProc : pkg;
938        }
939        if (separateProcesses != null) {
940            for (int i=separateProcesses.length-1; i>=0; i--) {
941                String sp = separateProcesses[i];
942                if (sp.equals(pkg) || sp.equals(defProc) || sp.equals(procSeq)) {
943                    return pkg;
944                }
945            }
946        }
947        if (procSeq == null || procSeq.length() <= 0) {
948            return defProc;
949        }
950        return buildCompoundName(pkg, procSeq, "package", outError);
951    }
952
953    private static String buildTaskAffinityName(String pkg, String defProc,
954            CharSequence procSeq, String[] outError) {
955        if (procSeq == null) {
956            return defProc;
957        }
958        if (procSeq.length() <= 0) {
959            return null;
960        }
961        return buildCompoundName(pkg, procSeq, "taskAffinity", outError);
962    }
963
964    private PermissionGroup parsePermissionGroup(Package owner, Resources res,
965            XmlPullParser parser, AttributeSet attrs, String[] outError)
966        throws XmlPullParserException, IOException {
967        PermissionGroup perm = new PermissionGroup(owner);
968
969        TypedArray sa = res.obtainAttributes(attrs,
970                com.android.internal.R.styleable.AndroidManifestPermissionGroup);
971
972        if (!parsePackageItemInfo(owner, perm.info, outError,
973                "<permission-group>", sa,
974                com.android.internal.R.styleable.AndroidManifestPermissionGroup_name,
975                com.android.internal.R.styleable.AndroidManifestPermissionGroup_label,
976                com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon)) {
977            sa.recycle();
978            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
979            return null;
980        }
981
982        perm.info.descriptionRes = sa.getResourceId(
983                com.android.internal.R.styleable.AndroidManifestPermissionGroup_description,
984                0);
985
986        sa.recycle();
987
988        if (!parseAllMetaData(res, parser, attrs, "<permission-group>", perm,
989                outError)) {
990            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
991            return null;
992        }
993
994        owner.permissionGroups.add(perm);
995
996        return perm;
997    }
998
999    private Permission parsePermission(Package owner, Resources res,
1000            XmlPullParser parser, AttributeSet attrs, String[] outError)
1001        throws XmlPullParserException, IOException {
1002        Permission perm = new Permission(owner);
1003
1004        TypedArray sa = res.obtainAttributes(attrs,
1005                com.android.internal.R.styleable.AndroidManifestPermission);
1006
1007        if (!parsePackageItemInfo(owner, perm.info, outError,
1008                "<permission>", sa,
1009                com.android.internal.R.styleable.AndroidManifestPermission_name,
1010                com.android.internal.R.styleable.AndroidManifestPermission_label,
1011                com.android.internal.R.styleable.AndroidManifestPermission_icon)) {
1012            sa.recycle();
1013            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1014            return null;
1015        }
1016
1017        perm.info.group = sa.getNonResourceString(
1018                com.android.internal.R.styleable.AndroidManifestPermission_permissionGroup);
1019        if (perm.info.group != null) {
1020            perm.info.group = perm.info.group.intern();
1021        }
1022
1023        perm.info.descriptionRes = sa.getResourceId(
1024                com.android.internal.R.styleable.AndroidManifestPermission_description,
1025                0);
1026
1027        perm.info.protectionLevel = sa.getInt(
1028                com.android.internal.R.styleable.AndroidManifestPermission_protectionLevel,
1029                PermissionInfo.PROTECTION_NORMAL);
1030
1031        sa.recycle();
1032
1033        if (perm.info.protectionLevel == -1) {
1034            outError[0] = "<permission> does not specify protectionLevel";
1035            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1036            return null;
1037        }
1038
1039        if (!parseAllMetaData(res, parser, attrs, "<permission>", perm,
1040                outError)) {
1041            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1042            return null;
1043        }
1044
1045        owner.permissions.add(perm);
1046
1047        return perm;
1048    }
1049
1050    private Permission parsePermissionTree(Package owner, Resources res,
1051            XmlPullParser parser, AttributeSet attrs, String[] outError)
1052        throws XmlPullParserException, IOException {
1053        Permission perm = new Permission(owner);
1054
1055        TypedArray sa = res.obtainAttributes(attrs,
1056                com.android.internal.R.styleable.AndroidManifestPermissionTree);
1057
1058        if (!parsePackageItemInfo(owner, perm.info, outError,
1059                "<permission-tree>", sa,
1060                com.android.internal.R.styleable.AndroidManifestPermissionTree_name,
1061                com.android.internal.R.styleable.AndroidManifestPermissionTree_label,
1062                com.android.internal.R.styleable.AndroidManifestPermissionTree_icon)) {
1063            sa.recycle();
1064            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1065            return null;
1066        }
1067
1068        sa.recycle();
1069
1070        int index = perm.info.name.indexOf('.');
1071        if (index > 0) {
1072            index = perm.info.name.indexOf('.', index+1);
1073        }
1074        if (index < 0) {
1075            outError[0] = "<permission-tree> name has less than three segments: "
1076                + perm.info.name;
1077            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1078            return null;
1079        }
1080
1081        perm.info.descriptionRes = 0;
1082        perm.info.protectionLevel = PermissionInfo.PROTECTION_NORMAL;
1083        perm.tree = true;
1084
1085        if (!parseAllMetaData(res, parser, attrs, "<permission-tree>", perm,
1086                outError)) {
1087            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1088            return null;
1089        }
1090
1091        owner.permissions.add(perm);
1092
1093        return perm;
1094    }
1095
1096    private Instrumentation parseInstrumentation(Package owner, Resources res,
1097            XmlPullParser parser, AttributeSet attrs, String[] outError)
1098        throws XmlPullParserException, IOException {
1099        TypedArray sa = res.obtainAttributes(attrs,
1100                com.android.internal.R.styleable.AndroidManifestInstrumentation);
1101
1102        if (mParseInstrumentationArgs == null) {
1103            mParseInstrumentationArgs = new ParsePackageItemArgs(owner, outError,
1104                    com.android.internal.R.styleable.AndroidManifestInstrumentation_name,
1105                    com.android.internal.R.styleable.AndroidManifestInstrumentation_label,
1106                    com.android.internal.R.styleable.AndroidManifestInstrumentation_icon);
1107            mParseInstrumentationArgs.tag = "<instrumentation>";
1108        }
1109
1110        mParseInstrumentationArgs.sa = sa;
1111
1112        Instrumentation a = new Instrumentation(mParseInstrumentationArgs,
1113                new InstrumentationInfo());
1114        if (outError[0] != null) {
1115            sa.recycle();
1116            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1117            return null;
1118        }
1119
1120        String str;
1121        str = sa.getNonResourceString(
1122                com.android.internal.R.styleable.AndroidManifestInstrumentation_targetPackage);
1123        a.info.targetPackage = str != null ? str.intern() : null;
1124
1125        a.info.handleProfiling = sa.getBoolean(
1126                com.android.internal.R.styleable.AndroidManifestInstrumentation_handleProfiling,
1127                false);
1128
1129        a.info.functionalTest = sa.getBoolean(
1130                com.android.internal.R.styleable.AndroidManifestInstrumentation_functionalTest,
1131                false);
1132
1133        sa.recycle();
1134
1135        if (a.info.targetPackage == null) {
1136            outError[0] = "<instrumentation> does not specify targetPackage";
1137            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1138            return null;
1139        }
1140
1141        if (!parseAllMetaData(res, parser, attrs, "<instrumentation>", a,
1142                outError)) {
1143            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1144            return null;
1145        }
1146
1147        owner.instrumentation.add(a);
1148
1149        return a;
1150    }
1151
1152    private boolean parseApplication(Package owner, Resources res,
1153            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
1154        throws XmlPullParserException, IOException {
1155        final ApplicationInfo ai = owner.applicationInfo;
1156        final String pkgName = owner.applicationInfo.packageName;
1157
1158        TypedArray sa = res.obtainAttributes(attrs,
1159                com.android.internal.R.styleable.AndroidManifestApplication);
1160
1161        String name = sa.getNonResourceString(
1162                com.android.internal.R.styleable.AndroidManifestApplication_name);
1163        if (name != null) {
1164            ai.className = buildClassName(pkgName, name, outError);
1165            if (ai.className == null) {
1166                sa.recycle();
1167                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1168                return false;
1169            }
1170        }
1171
1172        String manageSpaceActivity = sa.getNonResourceString(
1173                com.android.internal.R.styleable.AndroidManifestApplication_manageSpaceActivity);
1174        if (manageSpaceActivity != null) {
1175            ai.manageSpaceActivityName = buildClassName(pkgName, manageSpaceActivity,
1176                    outError);
1177        }
1178
1179        TypedValue v = sa.peekValue(
1180                com.android.internal.R.styleable.AndroidManifestApplication_label);
1181        if (v != null && (ai.labelRes=v.resourceId) == 0) {
1182            ai.nonLocalizedLabel = v.coerceToString();
1183        }
1184
1185        ai.icon = sa.getResourceId(
1186                com.android.internal.R.styleable.AndroidManifestApplication_icon, 0);
1187        ai.theme = sa.getResourceId(
1188                com.android.internal.R.styleable.AndroidManifestApplication_theme, 0);
1189        ai.descriptionRes = sa.getResourceId(
1190                com.android.internal.R.styleable.AndroidManifestApplication_description, 0);
1191
1192        if ((flags&PARSE_IS_SYSTEM) != 0) {
1193            if (sa.getBoolean(
1194                    com.android.internal.R.styleable.AndroidManifestApplication_persistent,
1195                    false)) {
1196                ai.flags |= ApplicationInfo.FLAG_PERSISTENT;
1197            }
1198        }
1199
1200        if (sa.getBoolean(
1201                com.android.internal.R.styleable.AndroidManifestApplication_debuggable,
1202                false)) {
1203            ai.flags |= ApplicationInfo.FLAG_DEBUGGABLE;
1204        }
1205
1206        if (sa.getBoolean(
1207                com.android.internal.R.styleable.AndroidManifestApplication_hasCode,
1208                true)) {
1209            ai.flags |= ApplicationInfo.FLAG_HAS_CODE;
1210        }
1211
1212        if (sa.getBoolean(
1213                com.android.internal.R.styleable.AndroidManifestApplication_allowTaskReparenting,
1214                false)) {
1215            ai.flags |= ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING;
1216        }
1217
1218        if (sa.getBoolean(
1219                com.android.internal.R.styleable.AndroidManifestApplication_allowClearUserData,
1220                true)) {
1221            ai.flags |= ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA;
1222        }
1223
1224        if (sa.getBoolean(
1225                com.android.internal.R.styleable.AndroidManifestApplication_testOnly,
1226                false)) {
1227            ai.flags |= ApplicationInfo.FLAG_TEST_ONLY;
1228        }
1229
1230        String str;
1231        str = sa.getNonResourceString(
1232                com.android.internal.R.styleable.AndroidManifestApplication_permission);
1233        ai.permission = (str != null && str.length() > 0) ? str.intern() : null;
1234
1235        str = sa.getNonResourceString(
1236                com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity);
1237        ai.taskAffinity = buildTaskAffinityName(ai.packageName, ai.packageName,
1238                str, outError);
1239
1240        if (outError[0] == null) {
1241            ai.processName = buildProcessName(ai.packageName, null, sa.getNonResourceString(
1242                    com.android.internal.R.styleable.AndroidManifestApplication_process),
1243                    flags, mSeparateProcesses, outError);
1244
1245            ai.enabled = sa.getBoolean(com.android.internal.R.styleable.AndroidManifestApplication_enabled, true);
1246        }
1247
1248        sa.recycle();
1249
1250        if (outError[0] != null) {
1251            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1252            return false;
1253        }
1254
1255        final int innerDepth = parser.getDepth();
1256
1257        int type;
1258        while ((type=parser.next()) != parser.END_DOCUMENT
1259               && (type != parser.END_TAG || parser.getDepth() > innerDepth)) {
1260            if (type == parser.END_TAG || type == parser.TEXT) {
1261                continue;
1262            }
1263
1264            String tagName = parser.getName();
1265            if (tagName.equals("activity")) {
1266                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false);
1267                if (a == null) {
1268                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1269                    return false;
1270                }
1271
1272                owner.activities.add(a);
1273
1274            } else if (tagName.equals("receiver")) {
1275                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true);
1276                if (a == null) {
1277                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1278                    return false;
1279                }
1280
1281                owner.receivers.add(a);
1282
1283            } else if (tagName.equals("service")) {
1284                Service s = parseService(owner, res, parser, attrs, flags, outError);
1285                if (s == null) {
1286                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1287                    return false;
1288                }
1289
1290                owner.services.add(s);
1291
1292            } else if (tagName.equals("provider")) {
1293                Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
1294                if (p == null) {
1295                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1296                    return false;
1297                }
1298
1299                owner.providers.add(p);
1300
1301            } else if (tagName.equals("activity-alias")) {
1302                Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);
1303                if (a == null) {
1304                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1305                    return false;
1306                }
1307
1308                owner.activities.add(a);
1309
1310            } else if (parser.getName().equals("meta-data")) {
1311                // note: application meta-data is stored off to the side, so it can
1312                // remain null in the primary copy (we like to avoid extra copies because
1313                // it can be large)
1314                if ((owner.mAppMetaData = parseMetaData(res, parser, attrs, owner.mAppMetaData,
1315                        outError)) == null) {
1316                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1317                    return false;
1318                }
1319
1320            } else if (tagName.equals("uses-library")) {
1321                sa = res.obtainAttributes(attrs,
1322                        com.android.internal.R.styleable.AndroidManifestUsesLibrary);
1323
1324                String lname = sa.getNonResourceString(
1325                        com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
1326
1327                sa.recycle();
1328
1329                if (lname != null && !owner.usesLibraries.contains(lname)) {
1330                    owner.usesLibraries.add(lname);
1331                }
1332
1333                XmlUtils.skipCurrentTag(parser);
1334
1335            } else if (tagName.equals("supports-density")) {
1336                sa = res.obtainAttributes(attrs,
1337                        com.android.internal.R.styleable.AndroidManifestSupportsDensity);
1338
1339                int density = sa.getInteger(
1340                        com.android.internal.R.styleable.AndroidManifestSupportsDensity_density, -1);
1341
1342                sa.recycle();
1343
1344                if (density != -1 && !owner.supportsDensityList.contains(density)) {
1345                    owner.supportsDensityList.add(density);
1346                }
1347
1348                XmlUtils.skipCurrentTag(parser);
1349
1350            } else {
1351                if (!RIGID_PARSER) {
1352                    Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
1353                    Log.w(TAG, "Unknown element under <application>: " + tagName);
1354                    XmlUtils.skipCurrentTag(parser);
1355                    continue;
1356                } else {
1357                    outError[0] = "Bad element under <application>: " + tagName;
1358                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1359                    return false;
1360                }
1361            }
1362        }
1363
1364        return true;
1365    }
1366
1367    private boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo,
1368            String[] outError, String tag, TypedArray sa,
1369            int nameRes, int labelRes, int iconRes) {
1370        String name = sa.getNonResourceString(nameRes);
1371        if (name == null) {
1372            outError[0] = tag + " does not specify android:name";
1373            return false;
1374        }
1375
1376        outInfo.name
1377            = buildClassName(owner.applicationInfo.packageName, name, outError);
1378        if (outInfo.name == null) {
1379            return false;
1380        }
1381
1382        int iconVal = sa.getResourceId(iconRes, 0);
1383        if (iconVal != 0) {
1384            outInfo.icon = iconVal;
1385            outInfo.nonLocalizedLabel = null;
1386        }
1387
1388        TypedValue v = sa.peekValue(labelRes);
1389        if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
1390            outInfo.nonLocalizedLabel = v.coerceToString();
1391        }
1392
1393        outInfo.packageName = owner.packageName;
1394
1395        return true;
1396    }
1397
1398    private boolean parseComponentInfo(Package owner, int flags,
1399            ComponentInfo outInfo, String[] outError, String tag, TypedArray sa,
1400            int nameRes, int labelRes, int iconRes, int processRes,
1401            int enabledRes) {
1402        if (!parsePackageItemInfo(owner, outInfo, outError, tag, sa,
1403                nameRes, labelRes, iconRes)) {
1404            return false;
1405        }
1406
1407        if (processRes != 0) {
1408            outInfo.processName = buildProcessName(owner.applicationInfo.packageName,
1409                    owner.applicationInfo.processName, sa.getNonResourceString(processRes),
1410                    flags, mSeparateProcesses, outError);
1411        }
1412        outInfo.enabled = sa.getBoolean(enabledRes, true);
1413
1414        return outError[0] == null;
1415    }
1416
1417    private Activity parseActivity(Package owner, Resources res,
1418            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError,
1419            boolean receiver) throws XmlPullParserException, IOException {
1420        TypedArray sa = res.obtainAttributes(attrs,
1421                com.android.internal.R.styleable.AndroidManifestActivity);
1422
1423        if (mParseActivityArgs == null) {
1424            mParseActivityArgs = new ParseComponentArgs(owner, outError,
1425                    com.android.internal.R.styleable.AndroidManifestActivity_name,
1426                    com.android.internal.R.styleable.AndroidManifestActivity_label,
1427                    com.android.internal.R.styleable.AndroidManifestActivity_icon,
1428                    mSeparateProcesses,
1429                    com.android.internal.R.styleable.AndroidManifestActivity_process,
1430                    com.android.internal.R.styleable.AndroidManifestActivity_enabled);
1431        }
1432
1433        mParseActivityArgs.tag = receiver ? "<receiver>" : "<activity>";
1434        mParseActivityArgs.sa = sa;
1435        mParseActivityArgs.flags = flags;
1436
1437        Activity a = new Activity(mParseActivityArgs, new ActivityInfo());
1438        if (outError[0] != null) {
1439            sa.recycle();
1440            return null;
1441        }
1442
1443        final boolean setExported = sa.hasValue(
1444                com.android.internal.R.styleable.AndroidManifestActivity_exported);
1445        if (setExported) {
1446            a.info.exported = sa.getBoolean(
1447                    com.android.internal.R.styleable.AndroidManifestActivity_exported, false);
1448        }
1449
1450        a.info.theme = sa.getResourceId(
1451                com.android.internal.R.styleable.AndroidManifestActivity_theme, 0);
1452
1453        String str;
1454        str = sa.getNonResourceString(
1455                com.android.internal.R.styleable.AndroidManifestActivity_permission);
1456        if (str == null) {
1457            a.info.permission = owner.applicationInfo.permission;
1458        } else {
1459            a.info.permission = str.length() > 0 ? str.toString().intern() : null;
1460        }
1461
1462        str = sa.getNonResourceString(
1463                com.android.internal.R.styleable.AndroidManifestActivity_taskAffinity);
1464        a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName,
1465                owner.applicationInfo.taskAffinity, str, outError);
1466
1467        a.info.flags = 0;
1468        if (sa.getBoolean(
1469                com.android.internal.R.styleable.AndroidManifestActivity_multiprocess,
1470                false)) {
1471            a.info.flags |= ActivityInfo.FLAG_MULTIPROCESS;
1472        }
1473
1474        if (sa.getBoolean(
1475                com.android.internal.R.styleable.AndroidManifestActivity_finishOnTaskLaunch,
1476                false)) {
1477            a.info.flags |= ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH;
1478        }
1479
1480        if (sa.getBoolean(
1481                com.android.internal.R.styleable.AndroidManifestActivity_clearTaskOnLaunch,
1482                false)) {
1483            a.info.flags |= ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH;
1484        }
1485
1486        if (sa.getBoolean(
1487                com.android.internal.R.styleable.AndroidManifestActivity_noHistory,
1488                false)) {
1489            a.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
1490        }
1491
1492        if (sa.getBoolean(
1493                com.android.internal.R.styleable.AndroidManifestActivity_alwaysRetainTaskState,
1494                false)) {
1495            a.info.flags |= ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE;
1496        }
1497
1498        if (sa.getBoolean(
1499                com.android.internal.R.styleable.AndroidManifestActivity_stateNotNeeded,
1500                false)) {
1501            a.info.flags |= ActivityInfo.FLAG_STATE_NOT_NEEDED;
1502        }
1503
1504        if (sa.getBoolean(
1505                com.android.internal.R.styleable.AndroidManifestActivity_excludeFromRecents,
1506                false)) {
1507            a.info.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
1508        }
1509
1510        if (sa.getBoolean(
1511                com.android.internal.R.styleable.AndroidManifestActivity_allowTaskReparenting,
1512                (owner.applicationInfo.flags&ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING) != 0)) {
1513            a.info.flags |= ActivityInfo.FLAG_ALLOW_TASK_REPARENTING;
1514        }
1515
1516        if (!receiver) {
1517            a.info.launchMode = sa.getInt(
1518                    com.android.internal.R.styleable.AndroidManifestActivity_launchMode,
1519                    ActivityInfo.LAUNCH_MULTIPLE);
1520            a.info.screenOrientation = sa.getInt(
1521                    com.android.internal.R.styleable.AndroidManifestActivity_screenOrientation,
1522                    ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1523            a.info.configChanges = sa.getInt(
1524                    com.android.internal.R.styleable.AndroidManifestActivity_configChanges,
1525                    0);
1526            a.info.softInputMode = sa.getInt(
1527                    com.android.internal.R.styleable.AndroidManifestActivity_windowSoftInputMode,
1528                    0);
1529        } else {
1530            a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
1531            a.info.configChanges = 0;
1532        }
1533
1534        sa.recycle();
1535
1536        if (outError[0] != null) {
1537            return null;
1538        }
1539
1540        int outerDepth = parser.getDepth();
1541        int type;
1542        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1543               && (type != XmlPullParser.END_TAG
1544                       || parser.getDepth() > outerDepth)) {
1545            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1546                continue;
1547            }
1548
1549            if (parser.getName().equals("intent-filter")) {
1550                ActivityIntentInfo intent = new ActivityIntentInfo(a);
1551                if (!parseIntent(res, parser, attrs, flags, intent, outError, !receiver)) {
1552                    return null;
1553                }
1554                if (intent.countActions() == 0) {
1555                    Log.w(TAG, "Intent filter for activity " + intent
1556                            + " defines no actions");
1557                } else {
1558                    a.intents.add(intent);
1559                }
1560            } else if (parser.getName().equals("meta-data")) {
1561                if ((a.metaData=parseMetaData(res, parser, attrs, a.metaData,
1562                        outError)) == null) {
1563                    return null;
1564                }
1565            } else {
1566                if (!RIGID_PARSER) {
1567                    Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
1568                    if (receiver) {
1569                        Log.w(TAG, "Unknown element under <receiver>: " + parser.getName());
1570                    } else {
1571                        Log.w(TAG, "Unknown element under <activity>: " + parser.getName());
1572                    }
1573                    XmlUtils.skipCurrentTag(parser);
1574                    continue;
1575                }
1576                if (receiver) {
1577                    outError[0] = "Bad element under <receiver>: " + parser.getName();
1578                } else {
1579                    outError[0] = "Bad element under <activity>: " + parser.getName();
1580                }
1581                return null;
1582            }
1583        }
1584
1585        if (!setExported) {
1586            a.info.exported = a.intents.size() > 0;
1587        }
1588
1589        return a;
1590    }
1591
1592    private Activity parseActivityAlias(Package owner, Resources res,
1593            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
1594            throws XmlPullParserException, IOException {
1595        TypedArray sa = res.obtainAttributes(attrs,
1596                com.android.internal.R.styleable.AndroidManifestActivityAlias);
1597
1598        String targetActivity = sa.getNonResourceString(
1599                com.android.internal.R.styleable.AndroidManifestActivityAlias_targetActivity);
1600        if (targetActivity == null) {
1601            outError[0] = "<activity-alias> does not specify android:targetActivity";
1602            sa.recycle();
1603            return null;
1604        }
1605
1606        targetActivity = buildClassName(owner.applicationInfo.packageName,
1607                targetActivity, outError);
1608        if (targetActivity == null) {
1609            sa.recycle();
1610            return null;
1611        }
1612
1613        if (mParseActivityAliasArgs == null) {
1614            mParseActivityAliasArgs = new ParseComponentArgs(owner, outError,
1615                    com.android.internal.R.styleable.AndroidManifestActivityAlias_name,
1616                    com.android.internal.R.styleable.AndroidManifestActivityAlias_label,
1617                    com.android.internal.R.styleable.AndroidManifestActivityAlias_icon,
1618                    mSeparateProcesses,
1619                    0,
1620                    com.android.internal.R.styleable.AndroidManifestActivityAlias_enabled);
1621            mParseActivityAliasArgs.tag = "<activity-alias>";
1622        }
1623
1624        mParseActivityAliasArgs.sa = sa;
1625        mParseActivityAliasArgs.flags = flags;
1626
1627        Activity target = null;
1628
1629        final int NA = owner.activities.size();
1630        for (int i=0; i<NA; i++) {
1631            Activity t = owner.activities.get(i);
1632            if (targetActivity.equals(t.info.name)) {
1633                target = t;
1634                break;
1635            }
1636        }
1637
1638        if (target == null) {
1639            outError[0] = "<activity-alias> target activity " + targetActivity
1640                    + " not found in manifest";
1641            sa.recycle();
1642            return null;
1643        }
1644
1645        ActivityInfo info = new ActivityInfo();
1646        info.targetActivity = targetActivity;
1647        info.configChanges = target.info.configChanges;
1648        info.flags = target.info.flags;
1649        info.icon = target.info.icon;
1650        info.labelRes = target.info.labelRes;
1651        info.nonLocalizedLabel = target.info.nonLocalizedLabel;
1652        info.launchMode = target.info.launchMode;
1653        info.processName = target.info.processName;
1654        info.screenOrientation = target.info.screenOrientation;
1655        info.taskAffinity = target.info.taskAffinity;
1656        info.theme = target.info.theme;
1657
1658        Activity a = new Activity(mParseActivityAliasArgs, info);
1659        if (outError[0] != null) {
1660            sa.recycle();
1661            return null;
1662        }
1663
1664        final boolean setExported = sa.hasValue(
1665                com.android.internal.R.styleable.AndroidManifestActivityAlias_exported);
1666        if (setExported) {
1667            a.info.exported = sa.getBoolean(
1668                    com.android.internal.R.styleable.AndroidManifestActivityAlias_exported, false);
1669        }
1670
1671        String str;
1672        str = sa.getNonResourceString(
1673                com.android.internal.R.styleable.AndroidManifestActivityAlias_permission);
1674        if (str != null) {
1675            a.info.permission = str.length() > 0 ? str.toString().intern() : null;
1676        }
1677
1678        sa.recycle();
1679
1680        if (outError[0] != null) {
1681            return null;
1682        }
1683
1684        int outerDepth = parser.getDepth();
1685        int type;
1686        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1687               && (type != XmlPullParser.END_TAG
1688                       || parser.getDepth() > outerDepth)) {
1689            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1690                continue;
1691            }
1692
1693            if (parser.getName().equals("intent-filter")) {
1694                ActivityIntentInfo intent = new ActivityIntentInfo(a);
1695                if (!parseIntent(res, parser, attrs, flags, intent, outError, true)) {
1696                    return null;
1697                }
1698                if (intent.countActions() == 0) {
1699                    Log.w(TAG, "Intent filter for activity alias " + intent
1700                            + " defines no actions");
1701                } else {
1702                    a.intents.add(intent);
1703                }
1704            } else if (parser.getName().equals("meta-data")) {
1705                if ((a.metaData=parseMetaData(res, parser, attrs, a.metaData,
1706                        outError)) == null) {
1707                    return null;
1708                }
1709            } else {
1710                if (!RIGID_PARSER) {
1711                    Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
1712                    Log.w(TAG, "Unknown element under <activity-alias>: " + parser.getName());
1713                    XmlUtils.skipCurrentTag(parser);
1714                    continue;
1715                }
1716                outError[0] = "Bad element under <activity-alias>: " + parser.getName();
1717                return null;
1718            }
1719        }
1720
1721        if (!setExported) {
1722            a.info.exported = a.intents.size() > 0;
1723        }
1724
1725        return a;
1726    }
1727
1728    private Provider parseProvider(Package owner, Resources res,
1729            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
1730            throws XmlPullParserException, IOException {
1731        TypedArray sa = res.obtainAttributes(attrs,
1732                com.android.internal.R.styleable.AndroidManifestProvider);
1733
1734        if (mParseProviderArgs == null) {
1735            mParseProviderArgs = new ParseComponentArgs(owner, outError,
1736                    com.android.internal.R.styleable.AndroidManifestProvider_name,
1737                    com.android.internal.R.styleable.AndroidManifestProvider_label,
1738                    com.android.internal.R.styleable.AndroidManifestProvider_icon,
1739                    mSeparateProcesses,
1740                    com.android.internal.R.styleable.AndroidManifestProvider_process,
1741                    com.android.internal.R.styleable.AndroidManifestProvider_enabled);
1742            mParseProviderArgs.tag = "<provider>";
1743        }
1744
1745        mParseProviderArgs.sa = sa;
1746        mParseProviderArgs.flags = flags;
1747
1748        Provider p = new Provider(mParseProviderArgs, new ProviderInfo());
1749        if (outError[0] != null) {
1750            sa.recycle();
1751            return null;
1752        }
1753
1754        p.info.exported = sa.getBoolean(
1755                com.android.internal.R.styleable.AndroidManifestProvider_exported, true);
1756
1757        String cpname = sa.getNonResourceString(
1758                com.android.internal.R.styleable.AndroidManifestProvider_authorities);
1759
1760        p.info.isSyncable = sa.getBoolean(
1761                com.android.internal.R.styleable.AndroidManifestProvider_syncable,
1762                false);
1763
1764        String permission = sa.getNonResourceString(
1765                com.android.internal.R.styleable.AndroidManifestProvider_permission);
1766        String str = sa.getNonResourceString(
1767                com.android.internal.R.styleable.AndroidManifestProvider_readPermission);
1768        if (str == null) {
1769            str = permission;
1770        }
1771        if (str == null) {
1772            p.info.readPermission = owner.applicationInfo.permission;
1773        } else {
1774            p.info.readPermission =
1775                str.length() > 0 ? str.toString().intern() : null;
1776        }
1777        str = sa.getNonResourceString(
1778                com.android.internal.R.styleable.AndroidManifestProvider_writePermission);
1779        if (str == null) {
1780            str = permission;
1781        }
1782        if (str == null) {
1783            p.info.writePermission = owner.applicationInfo.permission;
1784        } else {
1785            p.info.writePermission =
1786                str.length() > 0 ? str.toString().intern() : null;
1787        }
1788
1789        p.info.grantUriPermissions = sa.getBoolean(
1790                com.android.internal.R.styleable.AndroidManifestProvider_grantUriPermissions,
1791                false);
1792
1793        p.info.multiprocess = sa.getBoolean(
1794                com.android.internal.R.styleable.AndroidManifestProvider_multiprocess,
1795                false);
1796
1797        p.info.initOrder = sa.getInt(
1798                com.android.internal.R.styleable.AndroidManifestProvider_initOrder,
1799                0);
1800
1801        sa.recycle();
1802
1803        if (cpname == null) {
1804            outError[0] = "<provider> does not incude authorities attribute";
1805            return null;
1806        }
1807        p.info.authority = cpname.intern();
1808
1809        if (!parseProviderTags(res, parser, attrs, p, outError)) {
1810            return null;
1811        }
1812
1813        return p;
1814    }
1815
1816    private boolean parseProviderTags(Resources res,
1817            XmlPullParser parser, AttributeSet attrs,
1818            Provider outInfo, String[] outError)
1819            throws XmlPullParserException, IOException {
1820        int outerDepth = parser.getDepth();
1821        int type;
1822        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1823               && (type != XmlPullParser.END_TAG
1824                       || parser.getDepth() > outerDepth)) {
1825            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1826                continue;
1827            }
1828
1829            if (parser.getName().equals("meta-data")) {
1830                if ((outInfo.metaData=parseMetaData(res, parser, attrs,
1831                        outInfo.metaData, outError)) == null) {
1832                    return false;
1833                }
1834            } else if (parser.getName().equals("grant-uri-permission")) {
1835                TypedArray sa = res.obtainAttributes(attrs,
1836                        com.android.internal.R.styleable.AndroidManifestGrantUriPermission);
1837
1838                PatternMatcher pa = null;
1839
1840                String str = sa.getNonResourceString(
1841                        com.android.internal.R.styleable.AndroidManifestGrantUriPermission_path);
1842                if (str != null) {
1843                    pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL);
1844                }
1845
1846                str = sa.getNonResourceString(
1847                        com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPrefix);
1848                if (str != null) {
1849                    pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX);
1850                }
1851
1852                str = sa.getNonResourceString(
1853                        com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPattern);
1854                if (str != null) {
1855                    pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
1856                }
1857
1858                sa.recycle();
1859
1860                if (pa != null) {
1861                    if (outInfo.info.uriPermissionPatterns == null) {
1862                        outInfo.info.uriPermissionPatterns = new PatternMatcher[1];
1863                        outInfo.info.uriPermissionPatterns[0] = pa;
1864                    } else {
1865                        final int N = outInfo.info.uriPermissionPatterns.length;
1866                        PatternMatcher[] newp = new PatternMatcher[N+1];
1867                        System.arraycopy(outInfo.info.uriPermissionPatterns, 0, newp, 0, N);
1868                        newp[N] = pa;
1869                        outInfo.info.uriPermissionPatterns = newp;
1870                    }
1871                    outInfo.info.grantUriPermissions = true;
1872                }
1873                XmlUtils.skipCurrentTag(parser);
1874
1875            } else {
1876                if (!RIGID_PARSER) {
1877                    Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
1878                    Log.w(TAG, "Unknown element under <provider>: "
1879                            + parser.getName());
1880                    XmlUtils.skipCurrentTag(parser);
1881                    continue;
1882                }
1883                outError[0] = "Bad element under <provider>: "
1884                    + parser.getName();
1885                return false;
1886            }
1887        }
1888        return true;
1889    }
1890
1891    private Service parseService(Package owner, Resources res,
1892            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
1893            throws XmlPullParserException, IOException {
1894        TypedArray sa = res.obtainAttributes(attrs,
1895                com.android.internal.R.styleable.AndroidManifestService);
1896
1897        if (mParseServiceArgs == null) {
1898            mParseServiceArgs = new ParseComponentArgs(owner, outError,
1899                    com.android.internal.R.styleable.AndroidManifestService_name,
1900                    com.android.internal.R.styleable.AndroidManifestService_label,
1901                    com.android.internal.R.styleable.AndroidManifestService_icon,
1902                    mSeparateProcesses,
1903                    com.android.internal.R.styleable.AndroidManifestService_process,
1904                    com.android.internal.R.styleable.AndroidManifestService_enabled);
1905            mParseServiceArgs.tag = "<service>";
1906        }
1907
1908        mParseServiceArgs.sa = sa;
1909        mParseServiceArgs.flags = flags;
1910
1911        Service s = new Service(mParseServiceArgs, new ServiceInfo());
1912        if (outError[0] != null) {
1913            sa.recycle();
1914            return null;
1915        }
1916
1917        final boolean setExported = sa.hasValue(
1918                com.android.internal.R.styleable.AndroidManifestService_exported);
1919        if (setExported) {
1920            s.info.exported = sa.getBoolean(
1921                    com.android.internal.R.styleable.AndroidManifestService_exported, false);
1922        }
1923
1924        String str = sa.getNonResourceString(
1925                com.android.internal.R.styleable.AndroidManifestService_permission);
1926        if (str == null) {
1927            s.info.permission = owner.applicationInfo.permission;
1928        } else {
1929            s.info.permission = str.length() > 0 ? str.toString().intern() : null;
1930        }
1931
1932        sa.recycle();
1933
1934        int outerDepth = parser.getDepth();
1935        int type;
1936        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1937               && (type != XmlPullParser.END_TAG
1938                       || parser.getDepth() > outerDepth)) {
1939            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1940                continue;
1941            }
1942
1943            if (parser.getName().equals("intent-filter")) {
1944                ServiceIntentInfo intent = new ServiceIntentInfo(s);
1945                if (!parseIntent(res, parser, attrs, flags, intent, outError, false)) {
1946                    return null;
1947                }
1948
1949                s.intents.add(intent);
1950            } else if (parser.getName().equals("meta-data")) {
1951                if ((s.metaData=parseMetaData(res, parser, attrs, s.metaData,
1952                        outError)) == null) {
1953                    return null;
1954                }
1955            } else {
1956                if (!RIGID_PARSER) {
1957                    Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
1958                    Log.w(TAG, "Unknown element under <service>: "
1959                            + parser.getName());
1960                    XmlUtils.skipCurrentTag(parser);
1961                    continue;
1962                }
1963                outError[0] = "Bad element under <service>: "
1964                    + parser.getName();
1965                return null;
1966            }
1967        }
1968
1969        if (!setExported) {
1970            s.info.exported = s.intents.size() > 0;
1971        }
1972
1973        return s;
1974    }
1975
1976    private boolean parseAllMetaData(Resources res,
1977            XmlPullParser parser, AttributeSet attrs, String tag,
1978            Component outInfo, String[] outError)
1979            throws XmlPullParserException, IOException {
1980        int outerDepth = parser.getDepth();
1981        int type;
1982        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1983               && (type != XmlPullParser.END_TAG
1984                       || parser.getDepth() > outerDepth)) {
1985            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1986                continue;
1987            }
1988
1989            if (parser.getName().equals("meta-data")) {
1990                if ((outInfo.metaData=parseMetaData(res, parser, attrs,
1991                        outInfo.metaData, outError)) == null) {
1992                    return false;
1993                }
1994            } else {
1995                if (!RIGID_PARSER) {
1996                    Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
1997                    Log.w(TAG, "Unknown element under " + tag + ": "
1998                            + parser.getName());
1999                    XmlUtils.skipCurrentTag(parser);
2000                    continue;
2001                }
2002                outError[0] = "Bad element under " + tag + ": "
2003                    + parser.getName();
2004                return false;
2005            }
2006        }
2007        return true;
2008    }
2009
2010    private Bundle parseMetaData(Resources res,
2011            XmlPullParser parser, AttributeSet attrs,
2012            Bundle data, String[] outError)
2013            throws XmlPullParserException, IOException {
2014
2015        TypedArray sa = res.obtainAttributes(attrs,
2016                com.android.internal.R.styleable.AndroidManifestMetaData);
2017
2018        if (data == null) {
2019            data = new Bundle();
2020        }
2021
2022        String name = sa.getNonResourceString(
2023                com.android.internal.R.styleable.AndroidManifestMetaData_name);
2024        if (name == null) {
2025            outError[0] = "<meta-data> requires an android:name attribute";
2026            sa.recycle();
2027            return null;
2028        }
2029
2030        boolean success = true;
2031
2032        TypedValue v = sa.peekValue(
2033                com.android.internal.R.styleable.AndroidManifestMetaData_resource);
2034        if (v != null && v.resourceId != 0) {
2035            //Log.i(TAG, "Meta data ref " + name + ": " + v);
2036            data.putInt(name, v.resourceId);
2037        } else {
2038            v = sa.peekValue(
2039                    com.android.internal.R.styleable.AndroidManifestMetaData_value);
2040            //Log.i(TAG, "Meta data " + name + ": " + v);
2041            if (v != null) {
2042                if (v.type == TypedValue.TYPE_STRING) {
2043                    CharSequence cs = v.coerceToString();
2044                    data.putString(name, cs != null ? cs.toString() : null);
2045                } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
2046                    data.putBoolean(name, v.data != 0);
2047                } else if (v.type >= TypedValue.TYPE_FIRST_INT
2048                        && v.type <= TypedValue.TYPE_LAST_INT) {
2049                    data.putInt(name, v.data);
2050                } else if (v.type == TypedValue.TYPE_FLOAT) {
2051                    data.putFloat(name, v.getFloat());
2052                } else {
2053                    if (!RIGID_PARSER) {
2054                        Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
2055                        Log.w(TAG, "<meta-data> only supports string, integer, float, color, boolean, and resource reference types");
2056                    } else {
2057                        outError[0] = "<meta-data> only supports string, integer, float, color, boolean, and resource reference types";
2058                        data = null;
2059                    }
2060                }
2061            } else {
2062                outError[0] = "<meta-data> requires an android:value or android:resource attribute";
2063                data = null;
2064            }
2065        }
2066
2067        sa.recycle();
2068
2069        XmlUtils.skipCurrentTag(parser);
2070
2071        return data;
2072    }
2073
2074    private static final String ANDROID_RESOURCES
2075            = "http://schemas.android.com/apk/res/android";
2076
2077    private boolean parseIntent(Resources res,
2078            XmlPullParser parser, AttributeSet attrs, int flags,
2079            IntentInfo outInfo, String[] outError, boolean isActivity)
2080            throws XmlPullParserException, IOException {
2081
2082        TypedArray sa = res.obtainAttributes(attrs,
2083                com.android.internal.R.styleable.AndroidManifestIntentFilter);
2084
2085        int priority = sa.getInt(
2086                com.android.internal.R.styleable.AndroidManifestIntentFilter_priority, 0);
2087        if (priority > 0 && isActivity && (flags&PARSE_IS_SYSTEM) == 0) {
2088            Log.w(TAG, "Activity with priority > 0, forcing to 0 at "
2089                    + parser.getPositionDescription());
2090            priority = 0;
2091        }
2092        outInfo.setPriority(priority);
2093
2094        TypedValue v = sa.peekValue(
2095                com.android.internal.R.styleable.AndroidManifestIntentFilter_label);
2096        if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
2097            outInfo.nonLocalizedLabel = v.coerceToString();
2098        }
2099
2100        outInfo.icon = sa.getResourceId(
2101                com.android.internal.R.styleable.AndroidManifestIntentFilter_icon, 0);
2102
2103        sa.recycle();
2104
2105        int outerDepth = parser.getDepth();
2106        int type;
2107        while ((type=parser.next()) != parser.END_DOCUMENT
2108               && (type != parser.END_TAG || parser.getDepth() > outerDepth)) {
2109            if (type == parser.END_TAG || type == parser.TEXT) {
2110                continue;
2111            }
2112
2113            String nodeName = parser.getName();
2114            if (nodeName.equals("action")) {
2115                String value = attrs.getAttributeValue(
2116                        ANDROID_RESOURCES, "name");
2117                if (value == null || value == "") {
2118                    outError[0] = "No value supplied for <android:name>";
2119                    return false;
2120                }
2121                XmlUtils.skipCurrentTag(parser);
2122
2123                outInfo.addAction(value);
2124            } else if (nodeName.equals("category")) {
2125                String value = attrs.getAttributeValue(
2126                        ANDROID_RESOURCES, "name");
2127                if (value == null || value == "") {
2128                    outError[0] = "No value supplied for <android:name>";
2129                    return false;
2130                }
2131                XmlUtils.skipCurrentTag(parser);
2132
2133                outInfo.addCategory(value);
2134
2135            } else if (nodeName.equals("data")) {
2136                sa = res.obtainAttributes(attrs,
2137                        com.android.internal.R.styleable.AndroidManifestData);
2138
2139                String str = sa.getNonResourceString(
2140                        com.android.internal.R.styleable.AndroidManifestData_mimeType);
2141                if (str != null) {
2142                    try {
2143                        outInfo.addDataType(str);
2144                    } catch (IntentFilter.MalformedMimeTypeException e) {
2145                        outError[0] = e.toString();
2146                        sa.recycle();
2147                        return false;
2148                    }
2149                }
2150
2151                str = sa.getNonResourceString(
2152                        com.android.internal.R.styleable.AndroidManifestData_scheme);
2153                if (str != null) {
2154                    outInfo.addDataScheme(str);
2155                }
2156
2157                String host = sa.getNonResourceString(
2158                        com.android.internal.R.styleable.AndroidManifestData_host);
2159                String port = sa.getNonResourceString(
2160                        com.android.internal.R.styleable.AndroidManifestData_port);
2161                if (host != null) {
2162                    outInfo.addDataAuthority(host, port);
2163                }
2164
2165                str = sa.getNonResourceString(
2166                        com.android.internal.R.styleable.AndroidManifestData_path);
2167                if (str != null) {
2168                    outInfo.addDataPath(str, PatternMatcher.PATTERN_LITERAL);
2169                }
2170
2171                str = sa.getNonResourceString(
2172                        com.android.internal.R.styleable.AndroidManifestData_pathPrefix);
2173                if (str != null) {
2174                    outInfo.addDataPath(str, PatternMatcher.PATTERN_PREFIX);
2175                }
2176
2177                str = sa.getNonResourceString(
2178                        com.android.internal.R.styleable.AndroidManifestData_pathPattern);
2179                if (str != null) {
2180                    outInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
2181                }
2182
2183                sa.recycle();
2184                XmlUtils.skipCurrentTag(parser);
2185            } else if (!RIGID_PARSER) {
2186                Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
2187                Log.w(TAG, "Unknown element under <intent-filter>: " + parser.getName());
2188                XmlUtils.skipCurrentTag(parser);
2189            } else {
2190                outError[0] = "Bad element under <intent-filter>: " + parser.getName();
2191                return false;
2192            }
2193        }
2194
2195        outInfo.hasDefault = outInfo.hasCategory(Intent.CATEGORY_DEFAULT);
2196        if (false) {
2197            String cats = "";
2198            Iterator<String> it = outInfo.categoriesIterator();
2199            while (it != null && it.hasNext()) {
2200                cats += " " + it.next();
2201            }
2202            System.out.println("Intent d=" +
2203                    outInfo.hasDefault + ", cat=" + cats);
2204        }
2205
2206        return true;
2207    }
2208
2209    public final static class Package {
2210        public final String packageName;
2211
2212        // For now we only support one application per package.
2213        public final ApplicationInfo applicationInfo = new ApplicationInfo();
2214
2215        public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
2216        public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
2217        public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
2218        public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
2219        public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
2220        public final ArrayList<Service> services = new ArrayList<Service>(0);
2221        public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);
2222
2223        public final ArrayList<String> requestedPermissions = new ArrayList<String>();
2224
2225        public final ArrayList<String> usesLibraries = new ArrayList<String>();
2226        public String[] usesLibraryFiles = null;
2227
2228        // We store the application meta-data independently to avoid multiple unwanted references
2229        public Bundle mAppMetaData = null;
2230
2231        public final ArrayList<Integer> supportsDensityList = new ArrayList<Integer>();
2232        public int[] supportsDensities = null;
2233
2234        // If this is a 3rd party app, this is the path of the zip file.
2235        public String mPath;
2236
2237        // The version code declared for this package.
2238        public int mVersionCode;
2239
2240        // The version name declared for this package.
2241        public String mVersionName;
2242
2243        // The shared user id that this package wants to use.
2244        public String mSharedUserId;
2245
2246        // The shared user label that this package wants to use.
2247        public int mSharedUserLabel;
2248
2249        // Signatures that were read from the package.
2250        public Signature mSignatures[];
2251
2252        // For use by package manager service for quick lookup of
2253        // preferred up order.
2254        public int mPreferredOrder = 0;
2255
2256        // Additional data supplied by callers.
2257        public Object mExtras;
2258
2259        /*
2260         *  Applications hardware preferences
2261         */
2262        public final ArrayList<ConfigurationInfo> configPreferences =
2263                new ArrayList<ConfigurationInfo>();
2264
2265        public Package(String _name) {
2266            packageName = _name;
2267            applicationInfo.packageName = _name;
2268            applicationInfo.uid = -1;
2269        }
2270
2271        public String toString() {
2272            return "Package{"
2273                + Integer.toHexString(System.identityHashCode(this))
2274                + " " + packageName + "}";
2275        }
2276    }
2277
2278    public static class Component<II extends IntentInfo> {
2279        public final Package owner;
2280        public final ArrayList<II> intents;
2281        public final ComponentName component;
2282        public final String componentShortName;
2283        public Bundle metaData;
2284
2285        public Component(Package _owner) {
2286            owner = _owner;
2287            intents = null;
2288            component = null;
2289            componentShortName = null;
2290        }
2291
2292        public Component(final ParsePackageItemArgs args, final PackageItemInfo outInfo) {
2293            owner = args.owner;
2294            intents = new ArrayList<II>(0);
2295            String name = args.sa.getNonResourceString(args.nameRes);
2296            if (name == null) {
2297                component = null;
2298                componentShortName = null;
2299                args.outError[0] = args.tag + " does not specify android:name";
2300                return;
2301            }
2302
2303            outInfo.name
2304                = buildClassName(owner.applicationInfo.packageName, name, args.outError);
2305            if (outInfo.name == null) {
2306                component = null;
2307                componentShortName = null;
2308                args.outError[0] = args.tag + " does not have valid android:name";
2309                return;
2310            }
2311
2312            component = new ComponentName(owner.applicationInfo.packageName,
2313                    outInfo.name);
2314            componentShortName = component.flattenToShortString();
2315
2316            int iconVal = args.sa.getResourceId(args.iconRes, 0);
2317            if (iconVal != 0) {
2318                outInfo.icon = iconVal;
2319                outInfo.nonLocalizedLabel = null;
2320            }
2321
2322            TypedValue v = args.sa.peekValue(args.labelRes);
2323            if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
2324                outInfo.nonLocalizedLabel = v.coerceToString();
2325            }
2326
2327            outInfo.packageName = owner.packageName;
2328        }
2329
2330        public Component(final ParseComponentArgs args, final ComponentInfo outInfo) {
2331            this(args, (PackageItemInfo)outInfo);
2332            if (args.outError[0] != null) {
2333                return;
2334            }
2335
2336            if (args.processRes != 0) {
2337                outInfo.processName = buildProcessName(owner.applicationInfo.packageName,
2338                        owner.applicationInfo.processName, args.sa.getNonResourceString(args.processRes),
2339                        args.flags, args.sepProcesses, args.outError);
2340            }
2341            outInfo.enabled = args.sa.getBoolean(args.enabledRes, true);
2342        }
2343
2344        public Component(Component<II> clone) {
2345            owner = clone.owner;
2346            intents = clone.intents;
2347            component = clone.component;
2348            componentShortName = clone.componentShortName;
2349            metaData = clone.metaData;
2350        }
2351    }
2352
2353    public final static class Permission extends Component<IntentInfo> {
2354        public final PermissionInfo info;
2355        public boolean tree;
2356        public PermissionGroup group;
2357
2358        public Permission(Package _owner) {
2359            super(_owner);
2360            info = new PermissionInfo();
2361        }
2362
2363        public Permission(Package _owner, PermissionInfo _info) {
2364            super(_owner);
2365            info = _info;
2366        }
2367
2368        public String toString() {
2369            return "Permission{"
2370                + Integer.toHexString(System.identityHashCode(this))
2371                + " " + info.name + "}";
2372        }
2373    }
2374
2375    public final static class PermissionGroup extends Component<IntentInfo> {
2376        public final PermissionGroupInfo info;
2377
2378        public PermissionGroup(Package _owner) {
2379            super(_owner);
2380            info = new PermissionGroupInfo();
2381        }
2382
2383        public PermissionGroup(Package _owner, PermissionGroupInfo _info) {
2384            super(_owner);
2385            info = _info;
2386        }
2387
2388        public String toString() {
2389            return "PermissionGroup{"
2390                + Integer.toHexString(System.identityHashCode(this))
2391                + " " + info.name + "}";
2392        }
2393    }
2394
2395    private static boolean copyNeeded(int flags, Package p, Bundle metaData) {
2396        if ((flags & PackageManager.GET_META_DATA) != 0
2397                && (metaData != null || p.mAppMetaData != null)) {
2398            return true;
2399        }
2400        if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0
2401                && p.usesLibraryFiles != null) {
2402            return true;
2403        }
2404        if ((flags & PackageManager.GET_SUPPORTS_DENSITIES) != 0
2405            && p.supportsDensities != null) {
2406            return true;
2407        }
2408        return false;
2409    }
2410
2411    public static ApplicationInfo generateApplicationInfo(Package p, int flags) {
2412        if (p == null) return null;
2413        if (!copyNeeded(flags, p, null)) {
2414            return p.applicationInfo;
2415        }
2416
2417        // Make shallow copy so we can store the metadata/libraries safely
2418        ApplicationInfo ai = new ApplicationInfo(p.applicationInfo);
2419        if ((flags & PackageManager.GET_META_DATA) != 0) {
2420            ai.metaData = p.mAppMetaData;
2421        }
2422        if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0) {
2423            ai.sharedLibraryFiles = p.usesLibraryFiles;
2424        }
2425        if ((flags & PackageManager.GET_SUPPORTS_DENSITIES) != 0) {
2426            ai.supportsDensities = p.supportsDensities;
2427        }
2428        return ai;
2429    }
2430
2431    public static final PermissionInfo generatePermissionInfo(
2432            Permission p, int flags) {
2433        if (p == null) return null;
2434        if ((flags&PackageManager.GET_META_DATA) == 0) {
2435            return p.info;
2436        }
2437        PermissionInfo pi = new PermissionInfo(p.info);
2438        pi.metaData = p.metaData;
2439        return pi;
2440    }
2441
2442    public static final PermissionGroupInfo generatePermissionGroupInfo(
2443            PermissionGroup pg, int flags) {
2444        if (pg == null) return null;
2445        if ((flags&PackageManager.GET_META_DATA) == 0) {
2446            return pg.info;
2447        }
2448        PermissionGroupInfo pgi = new PermissionGroupInfo(pg.info);
2449        pgi.metaData = pg.metaData;
2450        return pgi;
2451    }
2452
2453    public final static class Activity extends Component<ActivityIntentInfo> {
2454        public final ActivityInfo info;
2455
2456        public Activity(final ParseComponentArgs args, final ActivityInfo _info) {
2457            super(args, _info);
2458            info = _info;
2459            info.applicationInfo = args.owner.applicationInfo;
2460        }
2461
2462        public String toString() {
2463            return "Activity{"
2464                + Integer.toHexString(System.identityHashCode(this))
2465                + " " + component.flattenToString() + "}";
2466        }
2467    }
2468
2469    public static final ActivityInfo generateActivityInfo(Activity a,
2470            int flags) {
2471        if (a == null) return null;
2472        if (!copyNeeded(flags, a.owner, a.metaData)) {
2473            return a.info;
2474        }
2475        // Make shallow copies so we can store the metadata safely
2476        ActivityInfo ai = new ActivityInfo(a.info);
2477        ai.metaData = a.metaData;
2478        ai.applicationInfo = generateApplicationInfo(a.owner, flags);
2479        return ai;
2480    }
2481
2482    public final static class Service extends Component<ServiceIntentInfo> {
2483        public final ServiceInfo info;
2484
2485        public Service(final ParseComponentArgs args, final ServiceInfo _info) {
2486            super(args, _info);
2487            info = _info;
2488            info.applicationInfo = args.owner.applicationInfo;
2489        }
2490
2491        public String toString() {
2492            return "Service{"
2493                + Integer.toHexString(System.identityHashCode(this))
2494                + " " + component.flattenToString() + "}";
2495        }
2496    }
2497
2498    public static final ServiceInfo generateServiceInfo(Service s, int flags) {
2499        if (s == null) return null;
2500        if (!copyNeeded(flags, s.owner, s.metaData)) {
2501            return s.info;
2502        }
2503        // Make shallow copies so we can store the metadata safely
2504        ServiceInfo si = new ServiceInfo(s.info);
2505        si.metaData = s.metaData;
2506        si.applicationInfo = generateApplicationInfo(s.owner, flags);
2507        return si;
2508    }
2509
2510    public final static class Provider extends Component {
2511        public final ProviderInfo info;
2512        public boolean syncable;
2513
2514        public Provider(final ParseComponentArgs args, final ProviderInfo _info) {
2515            super(args, _info);
2516            info = _info;
2517            info.applicationInfo = args.owner.applicationInfo;
2518            syncable = false;
2519        }
2520
2521        public Provider(Provider existingProvider) {
2522            super(existingProvider);
2523            this.info = existingProvider.info;
2524            this.syncable = existingProvider.syncable;
2525        }
2526
2527        public String toString() {
2528            return "Provider{"
2529                + Integer.toHexString(System.identityHashCode(this))
2530                + " " + info.name + "}";
2531        }
2532    }
2533
2534    public static final ProviderInfo generateProviderInfo(Provider p,
2535            int flags) {
2536        if (p == null) return null;
2537        if (!copyNeeded(flags, p.owner, p.metaData)
2538                && ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0
2539                        || p.info.uriPermissionPatterns == null)) {
2540            return p.info;
2541        }
2542        // Make shallow copies so we can store the metadata safely
2543        ProviderInfo pi = new ProviderInfo(p.info);
2544        pi.metaData = p.metaData;
2545        if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
2546            pi.uriPermissionPatterns = null;
2547        }
2548        pi.applicationInfo = generateApplicationInfo(p.owner, flags);
2549        return pi;
2550    }
2551
2552    public final static class Instrumentation extends Component {
2553        public final InstrumentationInfo info;
2554
2555        public Instrumentation(final ParsePackageItemArgs args, final InstrumentationInfo _info) {
2556            super(args, _info);
2557            info = _info;
2558        }
2559
2560        public String toString() {
2561            return "Instrumentation{"
2562                + Integer.toHexString(System.identityHashCode(this))
2563                + " " + component.flattenToString() + "}";
2564        }
2565    }
2566
2567    public static final InstrumentationInfo generateInstrumentationInfo(
2568            Instrumentation i, int flags) {
2569        if (i == null) return null;
2570        if ((flags&PackageManager.GET_META_DATA) == 0) {
2571            return i.info;
2572        }
2573        InstrumentationInfo ii = new InstrumentationInfo(i.info);
2574        ii.metaData = i.metaData;
2575        return ii;
2576    }
2577
2578    public static class IntentInfo extends IntentFilter {
2579        public boolean hasDefault;
2580        public int labelRes;
2581        public CharSequence nonLocalizedLabel;
2582        public int icon;
2583    }
2584
2585    public final static class ActivityIntentInfo extends IntentInfo {
2586        public final Activity activity;
2587
2588        public ActivityIntentInfo(Activity _activity) {
2589            activity = _activity;
2590        }
2591
2592        public String toString() {
2593            return "ActivityIntentInfo{"
2594                + Integer.toHexString(System.identityHashCode(this))
2595                + " " + activity.info.name + "}";
2596        }
2597    }
2598
2599    public final static class ServiceIntentInfo extends IntentInfo {
2600        public final Service service;
2601
2602        public ServiceIntentInfo(Service _service) {
2603            service = _service;
2604        }
2605
2606        public String toString() {
2607            return "ServiceIntentInfo{"
2608                + Integer.toHexString(System.identityHashCode(this))
2609                + " " + service.info.name + "}";
2610        }
2611    }
2612}
2613