PackageSignatures.java revision 420d58a9d867a3ce96fb4ea98bd30ee4d44eab4d
1/*
2 * Copyright (C) 2011 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 com.android.internal.util.XmlUtils;
20
21import org.xmlpull.v1.XmlPullParser;
22import org.xmlpull.v1.XmlPullParserException;
23import org.xmlpull.v1.XmlSerializer;
24
25import android.content.pm.PackageParser;
26import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
27import android.content.pm.Signature;
28import android.util.Log;
29
30import java.io.IOException;
31import java.util.ArrayList;
32
33class PackageSignatures {
34    Signature[] mSignatures;
35    @SignatureSchemeVersion int mSignatureSchemeVersion;
36
37    PackageSignatures(PackageSignatures orig) {
38        if (orig != null && orig.mSignatures != null) {
39            mSignatures = orig.mSignatures.clone();
40            mSignatureSchemeVersion = orig.mSignatureSchemeVersion;
41        }
42    }
43
44    PackageSignatures(PackageParser.SigningDetails signingDetails) {
45        assignSignatures(signingDetails);
46    }
47
48    PackageSignatures() {
49    }
50
51    void writeXml(XmlSerializer serializer, String tagName,
52            ArrayList<Signature> pastSignatures) throws IOException {
53        if (mSignatures == null) {
54            return;
55        }
56        serializer.startTag(null, tagName);
57        serializer.attribute(null, "count",
58                Integer.toString(mSignatures.length));
59        serializer.attribute(null, "schemeVersion", Integer.toString(mSignatureSchemeVersion));
60        for (int i=0; i<mSignatures.length; i++) {
61            serializer.startTag(null, "cert");
62            final Signature sig = mSignatures[i];
63            final int sigHash = sig.hashCode();
64            final int numPast = pastSignatures.size();
65            int j;
66            for (j=0; j<numPast; j++) {
67                Signature pastSig = pastSignatures.get(j);
68                if (pastSig.hashCode() == sigHash && pastSig.equals(sig)) {
69                    serializer.attribute(null, "index", Integer.toString(j));
70                    break;
71                }
72            }
73            if (j >= numPast) {
74                pastSignatures.add(sig);
75                serializer.attribute(null, "index", Integer.toString(numPast));
76                serializer.attribute(null, "key", sig.toCharsString());
77            }
78            serializer.endTag(null, "cert");
79        }
80        serializer.endTag(null, tagName);
81    }
82
83    void readXml(XmlPullParser parser, ArrayList<Signature> pastSignatures)
84            throws IOException, XmlPullParserException {
85        String countStr = parser.getAttributeValue(null, "count");
86        if (countStr == null) {
87            PackageManagerService.reportSettingsProblem(Log.WARN,
88                    "Error in package manager settings: <signatures> has"
89                       + " no count at " + parser.getPositionDescription());
90            XmlUtils.skipCurrentTag(parser);
91        }
92        String schemeVersionStr = parser.getAttributeValue(null, "schemeVersion");
93        if (schemeVersionStr == null) {
94            PackageManagerService.reportSettingsProblem(Log.WARN,
95                    "Error in package manager settings: <signatures> has no schemeVersion at "
96                        + parser.getPositionDescription());
97            mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
98        } else {
99            mSignatureSchemeVersion = Integer.parseInt(countStr);
100        }
101        final int count = Integer.parseInt(countStr);
102        mSignatures = new Signature[count];
103        int pos = 0;
104
105        int outerDepth = parser.getDepth();
106        int type;
107        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
108               && (type != XmlPullParser.END_TAG
109                       || parser.getDepth() > outerDepth)) {
110            if (type == XmlPullParser.END_TAG
111                    || type == XmlPullParser.TEXT) {
112                continue;
113            }
114
115            String tagName = parser.getName();
116            if (tagName.equals("cert")) {
117                if (pos < count) {
118                    String index = parser.getAttributeValue(null, "index");
119                    if (index != null) {
120                        try {
121                            int idx = Integer.parseInt(index);
122                            String key = parser.getAttributeValue(null, "key");
123                            if (key == null) {
124                                if (idx >= 0 && idx < pastSignatures.size()) {
125                                    Signature sig = pastSignatures.get(idx);
126                                    if (sig != null) {
127                                        mSignatures[pos] = pastSignatures.get(idx);
128                                        pos++;
129                                    } else {
130                                        PackageManagerService.reportSettingsProblem(Log.WARN,
131                                                "Error in package manager settings: <cert> "
132                                                   + "index " + index + " is not defined at "
133                                                   + parser.getPositionDescription());
134                                    }
135                                } else {
136                                    PackageManagerService.reportSettingsProblem(Log.WARN,
137                                            "Error in package manager settings: <cert> "
138                                               + "index " + index + " is out of bounds at "
139                                               + parser.getPositionDescription());
140                                }
141                            } else {
142                                while (pastSignatures.size() <= idx) {
143                                    pastSignatures.add(null);
144                                }
145                                Signature sig = new Signature(key);
146                                pastSignatures.set(idx, sig);
147                                mSignatures[pos] = sig;
148                                pos++;
149                            }
150                        } catch (NumberFormatException e) {
151                            PackageManagerService.reportSettingsProblem(Log.WARN,
152                                    "Error in package manager settings: <cert> "
153                                       + "index " + index + " is not a number at "
154                                       + parser.getPositionDescription());
155                        } catch (IllegalArgumentException e) {
156                            PackageManagerService.reportSettingsProblem(Log.WARN,
157                                    "Error in package manager settings: <cert> "
158                                       + "index " + index + " has an invalid signature at "
159                                       + parser.getPositionDescription() + ": "
160                                       + e.getMessage());
161                        }
162                    } else {
163                        PackageManagerService.reportSettingsProblem(Log.WARN,
164                                "Error in package manager settings: <cert> has"
165                                   + " no index at " + parser.getPositionDescription());
166                    }
167                } else {
168                    PackageManagerService.reportSettingsProblem(Log.WARN,
169                            "Error in package manager settings: too "
170                               + "many <cert> tags, expected " + count
171                               + " at " + parser.getPositionDescription());
172                }
173            } else {
174                PackageManagerService.reportSettingsProblem(Log.WARN,
175                        "Unknown element under <cert>: "
176                        + parser.getName());
177            }
178            XmlUtils.skipCurrentTag(parser);
179        }
180
181        if (pos < count) {
182            // Should never happen -- there is an error in the written
183            // settings -- but if it does we don't want to generate
184            // a bad array.
185            Signature[] newSigs = new Signature[pos];
186            System.arraycopy(mSignatures, 0, newSigs, 0, pos);
187            mSignatures = newSigs;
188        }
189    }
190
191    void assignSignatures(PackageParser.SigningDetails signingDetails) {
192        mSignatureSchemeVersion = signingDetails.signatureSchemeVersion;
193        if (!signingDetails.hasSignatures()) {
194            mSignatures = null;
195            return;
196        }
197        mSignatures = new Signature[signingDetails.signatures.length];
198        for (int i=0; i<signingDetails.signatures.length; i++) {
199            mSignatures[i] = signingDetails.signatures[i];
200        }
201    }
202
203    @Override
204    public String toString() {
205        StringBuffer buf = new StringBuffer(128);
206        buf.append("PackageSignatures{");
207        buf.append(Integer.toHexString(System.identityHashCode(this)));
208        buf.append(" version:");
209        buf.append(mSignatureSchemeVersion);
210        buf.append(", signatures:[");
211        if (mSignatures != null) {
212            for (int i=0; i<mSignatures.length; i++) {
213                if (i > 0) buf.append(", ");
214                buf.append(Integer.toHexString(
215                        mSignatures[i].hashCode()));
216            }
217        }
218        buf.append("]}");
219        return buf.toString();
220    }
221}