1/*
2 * Copyright (C) 2013 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 com.android.server.pm;
18
19import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
20
21import com.android.internal.util.Preconditions;
22import android.content.pm.PackageParser;
23import android.util.ArrayMap;
24import android.util.ArraySet;
25import android.util.Base64;
26import android.util.Slog;
27import android.util.LongSparseArray;
28
29import java.io.IOException;
30import java.io.PrintWriter;
31import java.security.PublicKey;
32import java.util.Set;
33
34import org.xmlpull.v1.XmlPullParser;
35import org.xmlpull.v1.XmlPullParserException;
36import org.xmlpull.v1.XmlSerializer;
37
38/*
39 * Manages system-wide KeySet state.
40 */
41public class KeySetManagerService {
42
43    static final String TAG = "KeySetManagerService";
44
45    /* original keysets implementation had no versioning info, so this is the first */
46    public static final int FIRST_VERSION = 1;
47
48    public static final int CURRENT_VERSION = FIRST_VERSION;
49
50    /** Sentinel value returned when a {@code KeySet} is not found. */
51    public static final long KEYSET_NOT_FOUND = -1;
52
53    /** Sentinel value returned when public key is not found. */
54    protected static final long PUBLIC_KEY_NOT_FOUND = -1;
55
56    private final LongSparseArray<KeySetHandle> mKeySets;
57
58    private final LongSparseArray<PublicKeyHandle> mPublicKeys;
59
60    protected final LongSparseArray<ArraySet<Long>> mKeySetMapping;
61
62    private final ArrayMap<String, PackageSetting> mPackages;
63
64    private long lastIssuedKeySetId = 0;
65
66    private long lastIssuedKeyId = 0;
67
68    class PublicKeyHandle {
69        private final PublicKey mKey;
70        private final long mId;
71        private int mRefCount;
72
73        public PublicKeyHandle(long id, PublicKey key) {
74            mId = id;
75            mRefCount = 1;
76            mKey = key;
77        }
78
79        /*
80         * Only used when reading state from packages.xml
81         */
82        private PublicKeyHandle(long id, int refCount, PublicKey key) {
83            mId = id;
84            mRefCount = refCount;
85            mKey = key;
86        }
87
88        public long getId() {
89            return mId;
90        }
91
92        public PublicKey getKey() {
93            return mKey;
94        }
95
96        public int getRefCountLPr() {
97            return mRefCount;
98        }
99
100        public void incrRefCountLPw() {
101            mRefCount++;
102            return;
103        }
104
105        public long decrRefCountLPw() {
106            mRefCount--;
107            return mRefCount;
108        }
109    }
110
111    public KeySetManagerService(ArrayMap<String, PackageSetting> packages) {
112        mKeySets = new LongSparseArray<KeySetHandle>();
113        mPublicKeys = new LongSparseArray<PublicKeyHandle>();
114        mKeySetMapping = new LongSparseArray<ArraySet<Long>>();
115        mPackages = packages;
116    }
117
118    /**
119     * Determine if a package is signed by the given KeySet.
120     *
121     * Returns false if the package was not signed by all the
122     * keys in the KeySet.
123     *
124     * Returns true if the package was signed by at least the
125     * keys in the given KeySet.
126     *
127     * Note that this can return true for multiple KeySets.
128     */
129    public boolean packageIsSignedByLPr(String packageName, KeySetHandle ks) {
130        PackageSetting pkg = mPackages.get(packageName);
131        if (pkg == null) {
132            throw new NullPointerException("Invalid package name");
133        }
134        if (pkg.keySetData == null) {
135            throw new NullPointerException("Package has no KeySet data");
136        }
137        long id = getIdByKeySetLPr(ks);
138        if (id == KEYSET_NOT_FOUND) {
139                return false;
140        }
141        ArraySet<Long> pkgKeys = mKeySetMapping.get(pkg.keySetData.getProperSigningKeySet());
142        ArraySet<Long> testKeys = mKeySetMapping.get(id);
143        return pkgKeys.containsAll(testKeys);
144    }
145
146    /**
147     * Determine if a package is signed by the given KeySet.
148     *
149     * Returns false if the package was not signed by all the
150     * keys in the KeySet, or if the package was signed by keys
151     * not in the KeySet.
152     *
153     * Note that this can return only for one KeySet.
154     */
155    public boolean packageIsSignedByExactlyLPr(String packageName, KeySetHandle ks) {
156        PackageSetting pkg = mPackages.get(packageName);
157        if (pkg == null) {
158            throw new NullPointerException("Invalid package name");
159        }
160        if (pkg.keySetData == null
161            || pkg.keySetData.getProperSigningKeySet()
162            == PackageKeySetData.KEYSET_UNASSIGNED) {
163            throw new NullPointerException("Package has no KeySet data");
164         }
165        long id = getIdByKeySetLPr(ks);
166        if (id == KEYSET_NOT_FOUND) {
167                return false;
168        }
169        ArraySet<Long> pkgKeys = mKeySetMapping.get(pkg.keySetData.getProperSigningKeySet());
170        ArraySet<Long> testKeys = mKeySetMapping.get(id);
171        return pkgKeys.equals(testKeys);
172    }
173
174    /**
175     * addScannedPackageLPw directly modifies the package metadata in  pm.Settings
176     * at a point of no-return.  We need to make sure that the scanned package does
177     * not contain bad keyset meta-data that could generate an incorrect
178     * PackageSetting. Verify that there is a signing keyset, there are no issues
179     * with null objects, and the upgrade and defined keysets match.
180     *
181     * Returns true if the package can safely be added to the keyset metadata.
182     */
183    public void assertScannedPackageValid(PackageParser.Package pkg)
184            throws PackageManagerException {
185        if (pkg == null || pkg.packageName == null) {
186            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
187                    "Passed invalid package to keyset validation.");
188        }
189        ArraySet<PublicKey> signingKeys = pkg.mSigningKeys;
190        if (signingKeys == null || !(signingKeys.size() > 0) || signingKeys.contains(null)) {
191            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
192                    "Package has invalid signing-key-set.");
193        }
194        ArrayMap<String, ArraySet<PublicKey>> definedMapping = pkg.mKeySetMapping;
195        if (definedMapping != null) {
196            if (definedMapping.containsKey(null) || definedMapping.containsValue(null)) {
197                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
198                        "Package has null defined key set.");
199            }
200            int defMapSize = definedMapping.size();
201            for (int i = 0; i < defMapSize; i++) {
202                if (!(definedMapping.valueAt(i).size() > 0)
203                        || definedMapping.valueAt(i).contains(null)) {
204                    throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
205                            "Package has null/no public keys for defined key-sets.");
206                }
207            }
208        }
209        ArraySet<String> upgradeAliases = pkg.mUpgradeKeySets;
210        if (upgradeAliases != null) {
211            if (definedMapping == null || !(definedMapping.keySet().containsAll(upgradeAliases))) {
212                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
213                        "Package has upgrade-key-sets without corresponding definitions.");
214            }
215        }
216    }
217
218    public void addScannedPackageLPw(PackageParser.Package pkg) {
219        Preconditions.checkNotNull(pkg, "Attempted to add null pkg to ksms.");
220        Preconditions.checkNotNull(pkg.packageName, "Attempted to add null pkg to ksms.");
221        PackageSetting ps = mPackages.get(pkg.packageName);
222        Preconditions.checkNotNull(ps, "pkg: " + pkg.packageName
223                    + "does not have a corresponding entry in mPackages.");
224        addSigningKeySetToPackageLPw(ps, pkg.mSigningKeys);
225        if (pkg.mKeySetMapping != null) {
226            addDefinedKeySetsToPackageLPw(ps, pkg.mKeySetMapping);
227            if (pkg.mUpgradeKeySets != null) {
228                addUpgradeKeySetsToPackageLPw(ps, pkg.mUpgradeKeySets);
229            }
230        }
231    }
232
233    /**
234     * Informs the system that the given package was signed by the provided KeySet.
235     */
236    void addSigningKeySetToPackageLPw(PackageSetting pkg,
237            ArraySet<PublicKey> signingKeys) {
238
239        /* check existing keyset for reuse or removal */
240        long signingKeySetId = pkg.keySetData.getProperSigningKeySet();
241
242        if (signingKeySetId != PackageKeySetData.KEYSET_UNASSIGNED) {
243            ArraySet<PublicKey> existingKeys = getPublicKeysFromKeySetLPr(signingKeySetId);
244            if (existingKeys != null && existingKeys.equals(signingKeys)) {
245
246                /* no change in signing keys, leave PackageSetting alone */
247                return;
248            } else {
249
250                /* old keyset no longer valid, remove ref */
251                decrementKeySetLPw(signingKeySetId);
252            }
253        }
254
255        /* create and add a new keyset */
256        KeySetHandle ks = addKeySetLPw(signingKeys);
257        long id = ks.getId();
258        pkg.keySetData.setProperSigningKeySet(id);
259        return;
260    }
261
262    /**
263     * Fetches the stable identifier associated with the given KeySet. Returns
264     * {@link #KEYSET_NOT_FOUND} if the KeySet... wasn't found.
265     */
266    private long getIdByKeySetLPr(KeySetHandle ks) {
267        for (int keySetIndex = 0; keySetIndex < mKeySets.size(); keySetIndex++) {
268            KeySetHandle value = mKeySets.valueAt(keySetIndex);
269            if (ks.equals(value)) {
270                return mKeySets.keyAt(keySetIndex);
271            }
272        }
273        return KEYSET_NOT_FOUND;
274    }
275
276    /**
277     * Inform the system that the given package defines the given KeySets.
278     * Remove any KeySets the package no longer defines.
279     */
280    void addDefinedKeySetsToPackageLPw(PackageSetting pkg,
281            ArrayMap<String, ArraySet<PublicKey>> definedMapping) {
282        ArrayMap<String, Long> prevDefinedKeySets = pkg.keySetData.getAliases();
283
284        /* add all of the newly defined KeySets */
285        ArrayMap<String, Long> newKeySetAliases = new ArrayMap<String, Long>();
286        final int defMapSize = definedMapping.size();
287        for (int i = 0; i < defMapSize; i++) {
288            String alias = definedMapping.keyAt(i);
289            ArraySet<PublicKey> pubKeys = definedMapping.valueAt(i);
290            if (alias != null && pubKeys != null || pubKeys.size() > 0) {
291                KeySetHandle ks = addKeySetLPw(pubKeys);
292                newKeySetAliases.put(alias, ks.getId());
293            }
294        }
295
296        /* remove each of the old references */
297        final int prevDefSize = prevDefinedKeySets.size();
298        for (int i = 0; i < prevDefSize; i++) {
299            decrementKeySetLPw(prevDefinedKeySets.valueAt(i));
300        }
301        pkg.keySetData.removeAllUpgradeKeySets();
302
303        /* switch to the just-added */
304        pkg.keySetData.setAliases(newKeySetAliases);
305        return;
306    }
307
308    /**
309     * This informs the system that the given package has defined a KeySet
310     * alias in its manifest to be an upgradeKeySet.  This must be called
311     * after all of the defined KeySets have been added.
312     */
313    void addUpgradeKeySetsToPackageLPw(PackageSetting pkg,
314            ArraySet<String> upgradeAliases) {
315        final int uaSize = upgradeAliases.size();
316        for (int i = 0; i < uaSize; i++) {
317            pkg.keySetData.addUpgradeKeySet(upgradeAliases.valueAt(i));
318        }
319        return;
320    }
321
322    /**
323     * Fetched the {@link KeySetHandle} that a given package refers to by the
324     * provided alias.  Returns null if the package is unknown or does not have a
325     * KeySet corresponding to that alias.
326     */
327    public KeySetHandle getKeySetByAliasAndPackageNameLPr(String packageName, String alias) {
328        PackageSetting p = mPackages.get(packageName);
329        if (p == null || p.keySetData == null) {
330            return null;
331        }
332        Long keySetId = p.keySetData.getAliases().get(alias);
333        if (keySetId == null) {
334            throw new IllegalArgumentException("Unknown KeySet alias: " + alias);
335        }
336        return mKeySets.get(keySetId);
337    }
338
339    /* Checks if an identifier refers to a known keyset */
340    public boolean isIdValidKeySetId(long id) {
341        return mKeySets.get(id) != null;
342    }
343
344    /**
345     * Fetches the {@link PublicKey public keys} which belong to the specified
346     * KeySet id.
347     *
348     * Returns {@code null} if the identifier doesn't
349     * identify a {@link KeySetHandle}.
350     */
351    public ArraySet<PublicKey> getPublicKeysFromKeySetLPr(long id) {
352        ArraySet<Long> pkIds = mKeySetMapping.get(id);
353        if (pkIds == null) {
354            return null;
355        }
356        ArraySet<PublicKey> mPubKeys = new ArraySet<PublicKey>();
357        final int pkSize = pkIds.size();
358        for (int i = 0; i < pkSize; i++) {
359            mPubKeys.add(mPublicKeys.get(pkIds.valueAt(i)).getKey());
360        }
361        return mPubKeys;
362    }
363
364    /**
365     * Fetches the proper {@link KeySetHandle KeySet} that signed the given
366     * package.
367     *
368     * @throws IllegalArgumentException if the package has no keyset data.
369     * @throws NullPointerException if the packgae is unknown.
370     */
371    public KeySetHandle  getSigningKeySetByPackageNameLPr(String packageName) {
372        PackageSetting p = mPackages.get(packageName);
373        if (p == null
374            || p.keySetData == null
375            || p.keySetData.getProperSigningKeySet()
376            == PackageKeySetData.KEYSET_UNASSIGNED) {
377            return null;
378        }
379        return mKeySets.get(p.keySetData.getProperSigningKeySet());
380    }
381
382    /**
383     * Creates a new KeySet corresponding to the given keys.
384     *
385     * If the {@link PublicKey PublicKeys} aren't known to the system, this
386     * adds them. Otherwise, they're deduped and the reference count
387     * incremented.
388     *
389     * If the KeySet isn't known to the system, this adds that and creates the
390     * mapping to the PublicKeys. If it is known, then it's deduped and the
391     * reference count is incremented.
392     *
393     * Throws if the provided set is {@code null}.
394     */
395    private KeySetHandle addKeySetLPw(ArraySet<PublicKey> keys) {
396        if (keys == null || keys.size() == 0) {
397            throw new IllegalArgumentException("Cannot add an empty set of keys!");
398        }
399
400        /* add each of the keys in the provided set */
401        ArraySet<Long> addedKeyIds = new ArraySet<Long>(keys.size());
402        final int kSize = keys.size();
403        for (int i = 0; i < kSize; i++) {
404            long id = addPublicKeyLPw(keys.valueAt(i));
405            addedKeyIds.add(id);
406        }
407
408        /* check to see if the resulting keyset is new */
409        long existingKeySetId = getIdFromKeyIdsLPr(addedKeyIds);
410        if (existingKeySetId != KEYSET_NOT_FOUND) {
411
412            /* public keys were incremented, but we aren't adding a new keyset: undo */
413            for (int i = 0; i < kSize; i++) {
414                decrementPublicKeyLPw(addedKeyIds.valueAt(i));
415            }
416            KeySetHandle ks = mKeySets.get(existingKeySetId);
417            ks.incrRefCountLPw();
418            return ks;
419        }
420
421        // get the next keyset id
422        long id = getFreeKeySetIDLPw();
423
424        // create the KeySet object and add to mKeySets and mapping
425        KeySetHandle ks = new KeySetHandle(id);
426        mKeySets.put(id, ks);
427        mKeySetMapping.put(id, addedKeyIds);
428        return ks;
429    }
430
431    /*
432     * Decrements the reference to KeySet represented by the given id.  If this
433     * drops to zero, then also decrement the reference to each public key it
434     * contains and remove the KeySet.
435     */
436    private void decrementKeySetLPw(long id) {
437        KeySetHandle ks = mKeySets.get(id);
438        if (ks == null) {
439            /* nothing to do */
440            return;
441        }
442        if (ks.decrRefCountLPw() <= 0) {
443            ArraySet<Long> pubKeys = mKeySetMapping.get(id);
444            final int pkSize = pubKeys.size();
445            for (int i = 0; i < pkSize; i++) {
446                decrementPublicKeyLPw(pubKeys.valueAt(i));
447            }
448            mKeySets.delete(id);
449            mKeySetMapping.delete(id);
450        }
451    }
452
453    /*
454     * Decrements the reference to PublicKey represented by the given id.  If
455     * this drops to zero, then remove it.
456     */
457    private void decrementPublicKeyLPw(long id) {
458        PublicKeyHandle pk = mPublicKeys.get(id);
459        if (pk == null) {
460            /* nothing to do */
461            return;
462        }
463        if (pk.decrRefCountLPw() <= 0) {
464            mPublicKeys.delete(id);
465        }
466    }
467
468    /**
469     * Adds the given PublicKey to the system, deduping as it goes.
470     */
471    private long addPublicKeyLPw(PublicKey key) {
472        Preconditions.checkNotNull(key, "Cannot add null public key!");
473        long id = getIdForPublicKeyLPr(key);
474        if (id != PUBLIC_KEY_NOT_FOUND) {
475
476            /* We already know about this key, increment its ref count and ret */
477            mPublicKeys.get(id).incrRefCountLPw();
478            return id;
479        }
480
481        /* if it's new find the first unoccupied slot in the public keys */
482        id = getFreePublicKeyIdLPw();
483        mPublicKeys.put(id, new PublicKeyHandle(id, key));
484        return id;
485    }
486
487    /**
488     * Finds the stable identifier for a KeySet based on a set of PublicKey stable IDs.
489     *
490     * Returns KEYSET_NOT_FOUND if there isn't one.
491     */
492    private long getIdFromKeyIdsLPr(Set<Long> publicKeyIds) {
493        for (int keyMapIndex = 0; keyMapIndex < mKeySetMapping.size(); keyMapIndex++) {
494            ArraySet<Long> value = mKeySetMapping.valueAt(keyMapIndex);
495            if (value.equals(publicKeyIds)) {
496                return mKeySetMapping.keyAt(keyMapIndex);
497            }
498        }
499        return KEYSET_NOT_FOUND;
500    }
501
502    /**
503     * Finds the stable identifier for a PublicKey or PUBLIC_KEY_NOT_FOUND.
504     */
505    private long getIdForPublicKeyLPr(PublicKey k) {
506        String encodedPublicKey = new String(k.getEncoded());
507        for (int publicKeyIndex = 0; publicKeyIndex < mPublicKeys.size(); publicKeyIndex++) {
508            PublicKey value = mPublicKeys.valueAt(publicKeyIndex).getKey();
509            String encodedExistingKey = new String(value.getEncoded());
510            if (encodedPublicKey.equals(encodedExistingKey)) {
511                return mPublicKeys.keyAt(publicKeyIndex);
512            }
513        }
514        return PUBLIC_KEY_NOT_FOUND;
515    }
516
517    /**
518     * Gets an unused stable identifier for a KeySet.
519     */
520    private long getFreeKeySetIDLPw() {
521        lastIssuedKeySetId += 1;
522        return lastIssuedKeySetId;
523    }
524
525    /**
526     * Same as above, but for public keys.
527     */
528    private long getFreePublicKeyIdLPw() {
529        lastIssuedKeyId += 1;
530        return lastIssuedKeyId;
531    }
532
533    /*
534     * This package is being removed from the system, so we need to
535     * remove its keyset and public key references, then remove its
536     * keyset data.
537     */
538    public void removeAppKeySetDataLPw(String packageName) {
539
540        /* remove refs from common keysets and public keys */
541        PackageSetting pkg = mPackages.get(packageName);
542        Preconditions.checkNotNull(pkg, "pkg name: " + packageName
543                + "does not have a corresponding entry in mPackages.");
544        long signingKeySetId = pkg.keySetData.getProperSigningKeySet();
545        decrementKeySetLPw(signingKeySetId);
546        ArrayMap<String, Long> definedKeySets = pkg.keySetData.getAliases();
547        for (int i = 0; i < definedKeySets.size(); i++) {
548            decrementKeySetLPw(definedKeySets.valueAt(i));
549        }
550
551        /* remove from package */
552        clearPackageKeySetDataLPw(pkg);
553        return;
554    }
555
556    private void clearPackageKeySetDataLPw(PackageSetting pkg) {
557        pkg.keySetData.setProperSigningKeySet(PackageKeySetData.KEYSET_UNASSIGNED);
558        pkg.keySetData.removeAllDefinedKeySets();
559        pkg.keySetData.removeAllUpgradeKeySets();
560        return;
561    }
562
563    public String encodePublicKey(PublicKey k) throws IOException {
564        return new String(Base64.encode(k.getEncoded(), Base64.NO_WRAP));
565    }
566
567    public void dumpLPr(PrintWriter pw, String packageName,
568                        PackageManagerService.DumpState dumpState) {
569        boolean printedHeader = false;
570        for (ArrayMap.Entry<String, PackageSetting> e : mPackages.entrySet()) {
571            String keySetPackage = e.getKey();
572            if (packageName != null && !packageName.equals(keySetPackage)) {
573                continue;
574            }
575            if (!printedHeader) {
576                if (dumpState.onTitlePrinted())
577                    pw.println();
578                pw.println("Key Set Manager:");
579                printedHeader = true;
580            }
581            PackageSetting pkg = e.getValue();
582            pw.print("  ["); pw.print(keySetPackage); pw.println("]");
583            if (pkg.keySetData != null) {
584                boolean printedLabel = false;
585                for (ArrayMap.Entry<String, Long> entry : pkg.keySetData.getAliases().entrySet()) {
586                    if (!printedLabel) {
587                        pw.print("      KeySets Aliases: ");
588                        printedLabel = true;
589                    } else {
590                        pw.print(", ");
591                    }
592                    pw.print(entry.getKey());
593                    pw.print('=');
594                    pw.print(Long.toString(entry.getValue()));
595                }
596                if (printedLabel) {
597                    pw.println("");
598                }
599                printedLabel = false;
600                if (pkg.keySetData.isUsingDefinedKeySets()) {
601                    ArrayMap<String, Long> definedKeySets = pkg.keySetData.getAliases();
602                    final int dksSize = definedKeySets.size();
603                    for (int i = 0; i < dksSize; i++) {
604                        if (!printedLabel) {
605                            pw.print("      Defined KeySets: ");
606                            printedLabel = true;
607                        } else {
608                            pw.print(", ");
609                        }
610                        pw.print(Long.toString(definedKeySets.valueAt(i)));
611                    }
612                }
613                if (printedLabel) {
614                    pw.println("");
615                }
616                printedLabel = false;
617                final long signingKeySet = pkg.keySetData.getProperSigningKeySet();
618                pw.print("      Signing KeySets: ");
619                pw.print(Long.toString(signingKeySet));
620                pw.println("");
621                if (pkg.keySetData.isUsingUpgradeKeySets()) {
622                    for (long keySetId : pkg.keySetData.getUpgradeKeySets()) {
623                        if (!printedLabel) {
624                            pw.print("      Upgrade KeySets: ");
625                            printedLabel = true;
626                        } else {
627                            pw.print(", ");
628                        }
629                        pw.print(Long.toString(keySetId));
630                    }
631                }
632                if (printedLabel) {
633                    pw.println("");
634                }
635            }
636        }
637    }
638
639    void writeKeySetManagerServiceLPr(XmlSerializer serializer) throws IOException {
640        serializer.startTag(null, "keyset-settings");
641        serializer.attribute(null, "version", Integer.toString(CURRENT_VERSION));
642        writePublicKeysLPr(serializer);
643        writeKeySetsLPr(serializer);
644        serializer.startTag(null, "lastIssuedKeyId");
645        serializer.attribute(null, "value", Long.toString(lastIssuedKeyId));
646        serializer.endTag(null, "lastIssuedKeyId");
647        serializer.startTag(null, "lastIssuedKeySetId");
648        serializer.attribute(null, "value", Long.toString(lastIssuedKeySetId));
649        serializer.endTag(null, "lastIssuedKeySetId");
650        serializer.endTag(null, "keyset-settings");
651    }
652
653    void writePublicKeysLPr(XmlSerializer serializer) throws IOException {
654        serializer.startTag(null, "keys");
655        for (int pKeyIndex = 0; pKeyIndex < mPublicKeys.size(); pKeyIndex++) {
656            long id = mPublicKeys.keyAt(pKeyIndex);
657            PublicKeyHandle pkh = mPublicKeys.valueAt(pKeyIndex);
658            String encodedKey = encodePublicKey(pkh.getKey());
659            serializer.startTag(null, "public-key");
660            serializer.attribute(null, "identifier", Long.toString(id));
661            serializer.attribute(null, "value", encodedKey);
662            serializer.endTag(null, "public-key");
663        }
664        serializer.endTag(null, "keys");
665    }
666
667    void writeKeySetsLPr(XmlSerializer serializer) throws IOException {
668        serializer.startTag(null, "keysets");
669        for (int keySetIndex = 0; keySetIndex < mKeySetMapping.size(); keySetIndex++) {
670            long id = mKeySetMapping.keyAt(keySetIndex);
671            ArraySet<Long> keys = mKeySetMapping.valueAt(keySetIndex);
672            serializer.startTag(null, "keyset");
673            serializer.attribute(null, "identifier", Long.toString(id));
674            for (long keyId : keys) {
675                serializer.startTag(null, "key-id");
676                serializer.attribute(null, "identifier", Long.toString(keyId));
677                serializer.endTag(null, "key-id");
678            }
679            serializer.endTag(null, "keyset");
680        }
681        serializer.endTag(null, "keysets");
682    }
683
684    void readKeySetsLPw(XmlPullParser parser, ArrayMap<Long, Integer> keySetRefCounts)
685            throws XmlPullParserException, IOException {
686        int type;
687        long currentKeySetId = 0;
688        int outerDepth = parser.getDepth();
689        String recordedVersionStr = parser.getAttributeValue(null, "version");
690        if (recordedVersionStr == null) {
691            // The keyset information comes from pre-versioned devices, and
692            // is inaccurate, don't collect any of it.
693            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
694                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
695                continue;
696            }
697            // The KeySet information read previously from packages.xml is invalid.
698            // Destroy it all.
699            for (PackageSetting p : mPackages.values()) {
700                clearPackageKeySetDataLPw(p);
701            }
702            return;
703        }
704        int recordedVersion = Integer.parseInt(recordedVersionStr);
705        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
706               && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
707            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
708                continue;
709            }
710            final String tagName = parser.getName();
711            if (tagName.equals("keys")) {
712                readKeysLPw(parser);
713            } else if (tagName.equals("keysets")) {
714                readKeySetListLPw(parser);
715            } else if (tagName.equals("lastIssuedKeyId")) {
716                lastIssuedKeyId = Long.parseLong(parser.getAttributeValue(null, "value"));
717            } else if (tagName.equals("lastIssuedKeySetId")) {
718                lastIssuedKeySetId = Long.parseLong(parser.getAttributeValue(null, "value"));
719            }
720        }
721
722        addRefCountsFromSavedPackagesLPw(keySetRefCounts);
723    }
724
725    void readKeysLPw(XmlPullParser parser)
726            throws XmlPullParserException, IOException {
727        int outerDepth = parser.getDepth();
728        int type;
729        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
730                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
731            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
732                continue;
733            }
734            final String tagName = parser.getName();
735            if (tagName.equals("public-key")) {
736                readPublicKeyLPw(parser);
737            }
738        }
739    }
740
741    void readKeySetListLPw(XmlPullParser parser)
742            throws XmlPullParserException, IOException {
743        int outerDepth = parser.getDepth();
744        int type;
745        long currentKeySetId = 0;
746        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
747                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
748            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
749                continue;
750            }
751            final String tagName = parser.getName();
752            if (tagName.equals("keyset")) {
753                String encodedID = parser.getAttributeValue(null, "identifier");
754                currentKeySetId = Long.parseLong(encodedID);
755                int refCount = 0;
756                mKeySets.put(currentKeySetId, new KeySetHandle(currentKeySetId, refCount));
757                mKeySetMapping.put(currentKeySetId, new ArraySet<Long>());
758            } else if (tagName.equals("key-id")) {
759                String encodedID = parser.getAttributeValue(null, "identifier");
760                long id = Long.parseLong(encodedID);
761                mKeySetMapping.get(currentKeySetId).add(id);
762            }
763        }
764    }
765
766    void readPublicKeyLPw(XmlPullParser parser)
767            throws XmlPullParserException {
768        String encodedID = parser.getAttributeValue(null, "identifier");
769        long identifier = Long.parseLong(encodedID);
770        int refCount = 0;
771        String encodedPublicKey = parser.getAttributeValue(null, "value");
772        PublicKey pub = PackageParser.parsePublicKey(encodedPublicKey);
773        if (pub != null) {
774            PublicKeyHandle pkh = new PublicKeyHandle(identifier, refCount, pub);
775            mPublicKeys.put(identifier, pkh);
776        }
777    }
778
779    /*
780     * Set each KeySet ref count.  Also increment all public keys in each keyset.
781     */
782    private void addRefCountsFromSavedPackagesLPw(ArrayMap<Long, Integer> keySetRefCounts) {
783        final int numRefCounts = keySetRefCounts.size();
784        for (int i = 0; i < numRefCounts; i++) {
785            KeySetHandle ks = mKeySets.get(keySetRefCounts.keyAt(i));
786            if (ks == null) {
787                /* something went terribly wrong and we have references to a non-existent key-set */
788                Slog.wtf(TAG, "Encountered non-existent key-set reference when reading settings");
789                continue;
790            }
791            ks.setRefCountLPw(keySetRefCounts.valueAt(i));
792        }
793
794        /*
795         * In case something went terribly wrong and we have keysets with no associated packges
796         * that refer to them, record the orphaned keyset ids, and remove them using
797         * decrementKeySetLPw() after all keyset references have been set so that the associtaed
798         * public keys have the appropriate references from all keysets.
799         */
800        ArraySet<Long> orphanedKeySets = new ArraySet<Long>();
801        final int numKeySets = mKeySets.size();
802        for (int i = 0; i < numKeySets; i++) {
803            if (mKeySets.valueAt(i).getRefCountLPr() == 0) {
804                Slog.wtf(TAG, "Encountered key-set w/out package references when reading settings");
805                orphanedKeySets.add(mKeySets.keyAt(i));
806            }
807            ArraySet<Long> pubKeys = mKeySetMapping.valueAt(i);
808            final int pkSize = pubKeys.size();
809            for (int j = 0; j < pkSize; j++) {
810                mPublicKeys.get(pubKeys.valueAt(j)).incrRefCountLPw();
811            }
812        }
813        final int numOrphans = orphanedKeySets.size();
814        for (int i = 0; i < numOrphans; i++) {
815            decrementKeySetLPw(orphanedKeySets.valueAt(i));
816        }
817    }
818}
819