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