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