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