ShortcutPackageInfo.java revision 9da23fc6ac565b38129d52f4f8f174c833a9bd01
1/*
2 * Copyright (C) 2016 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 */
16package com.android.server.pm;
17
18import android.annotation.UserIdInt;
19import android.content.pm.PackageInfo;
20import android.util.Slog;
21
22import com.android.server.backup.BackupUtils;
23
24import libcore.io.Base64;
25import libcore.util.HexEncoding;
26
27import org.xmlpull.v1.XmlPullParser;
28import org.xmlpull.v1.XmlPullParserException;
29import org.xmlpull.v1.XmlSerializer;
30
31import java.io.IOException;
32import java.io.PrintWriter;
33import java.util.ArrayList;
34
35/**
36 * Package information used by {@link android.content.pm.ShortcutManager} for backup / restore.
37 */
38class ShortcutPackageInfo {
39    private static final String TAG = ShortcutService.TAG;
40
41    static final String TAG_ROOT = "package-info";
42    private static final String ATTR_VERSION = "version";
43    private static final String ATTR_SHADOW = "shadow";
44
45    private static final String TAG_SIGNATURE = "signature";
46    private static final String ATTR_SIGNATURE_HASH = "hash";
47
48    /**
49     * When true, this package information was restored from the previous device, and the app hasn't
50     * been installed yet.
51     */
52    private boolean mIsShadow;
53    private int mVersionCode;
54    private ArrayList<byte[]> mSigHashes;
55
56    private ShortcutPackageInfo(int versionCode, ArrayList<byte[]> sigHashes, boolean isShadow) {
57        mVersionCode = versionCode;
58        mIsShadow = isShadow;
59        mSigHashes = sigHashes;
60    }
61
62    public static ShortcutPackageInfo newEmpty() {
63        return new ShortcutPackageInfo(0, new ArrayList<>(0), /* isShadow */ false);
64    }
65
66    public boolean isShadow() {
67        return mIsShadow;
68    }
69
70    public boolean isInstalled() {
71        return !mIsShadow;
72    }
73
74    public void setShadow(boolean shadow) {
75        mIsShadow = shadow;
76    }
77
78    public int getVersionCode() {
79        return mVersionCode;
80    }
81
82    public boolean canRestoreTo(PackageInfo target) {
83        if (target.versionCode < mVersionCode) {
84            Slog.w(TAG, String.format("Package current version %d < backed up version %d",
85                    target.versionCode, mVersionCode));
86            return false;
87        }
88        if (!BackupUtils.signaturesMatch(mSigHashes, target)) {
89            Slog.w(TAG, "Package signature mismtach");
90            return false;
91        }
92        return true;
93    }
94
95    public static ShortcutPackageInfo generateForInstalledPackage(
96            ShortcutService s, String packageName, @UserIdInt int packageUserId) {
97        final PackageInfo pi = s.getPackageInfoWithSignatures(packageName, packageUserId);
98        if (pi.signatures == null || pi.signatures.length == 0) {
99            Slog.e(TAG, "Can't get signatures: package=" + packageName);
100            return null;
101        }
102        final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.versionCode,
103                BackupUtils.hashSignatureArray(pi.signatures), /* shadow=*/ false);
104
105        return ret;
106    }
107
108    public void refresh(ShortcutService s, ShortcutPackageItem pkg) {
109        // Note use mUserId here, rather than userId.
110        final PackageInfo pi = s.getPackageInfoWithSignatures(
111                pkg.getPackageName(), pkg.getPackageUserId());
112        if (pi == null) {
113            Slog.w(TAG, "Package not found: " + pkg.getPackageName());
114            return;
115        }
116        mVersionCode = pi.versionCode;
117        mSigHashes = BackupUtils.hashSignatureArray(pi.signatures);
118    }
119
120    public void saveToXml(XmlSerializer out) throws IOException {
121
122        out.startTag(null, TAG_ROOT);
123
124        ShortcutService.writeAttr(out, ATTR_VERSION, mVersionCode);
125        ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow);
126
127        for (int i = 0; i < mSigHashes.size(); i++) {
128            out.startTag(null, TAG_SIGNATURE);
129            ShortcutService.writeAttr(out, ATTR_SIGNATURE_HASH, Base64.encode(mSigHashes.get(i)));
130            out.endTag(null, TAG_SIGNATURE);
131        }
132        out.endTag(null, TAG_ROOT);
133    }
134
135    public static ShortcutPackageInfo loadFromXml(XmlPullParser parser)
136            throws IOException, XmlPullParserException {
137
138        final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION);
139        final boolean shadow = ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW);
140
141        final ArrayList<byte[]> hashes = new ArrayList<>();
142
143
144        final int outerDepth = parser.getDepth();
145        int type;
146        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
147                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
148            if (type != XmlPullParser.START_TAG) {
149                continue;
150            }
151            final int depth = parser.getDepth();
152            final String tag = parser.getName();
153
154            if (depth == outerDepth + 1) {
155                switch (tag) {
156                    case TAG_SIGNATURE: {
157                        final String hash = ShortcutService.parseStringAttribute(
158                                parser, ATTR_SIGNATURE_HASH);
159                        hashes.add(Base64.decode(hash.getBytes()));
160                        continue;
161                    }
162                }
163            }
164            ShortcutService.warnForInvalidTag(depth, tag);
165        }
166        return new ShortcutPackageInfo(versionCode, hashes, shadow);
167    }
168
169    public void dump(ShortcutService s, PrintWriter pw, String prefix) {
170        pw.println();
171
172        pw.print(prefix);
173        pw.println("PackageInfo:");
174
175        pw.print(prefix);
176        pw.print("  IsShadow: ");
177        pw.print(mIsShadow);
178        pw.println();
179
180        pw.print(prefix);
181        pw.print("  Version: ");
182        pw.print(mVersionCode);
183        pw.println();
184
185        for (int i = 0; i < mSigHashes.size(); i++) {
186            pw.print(prefix);
187            pw.print("    ");
188            pw.print("SigHash: ");
189            pw.println(HexEncoding.encode(mSigHashes.get(i)));
190        }
191    }
192}
193