ShortcutPackageInfo.java revision 2e210c4d0f766e52ea4c087a1d54213c36a4e0ea
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 void setShadow(boolean shadow) {
71        mIsShadow = shadow;
72    }
73
74    public int getVersionCode() {
75        return mVersionCode;
76    }
77
78    public boolean hasSignatures() {
79        return mSigHashes.size() > 0;
80    }
81
82    public boolean canRestoreTo(ShortcutService s, PackageInfo target) {
83        if (!s.shouldBackupApp(target)) {
84            // "allowBackup" was true when backed up, but now false.
85            Slog.w(TAG, "Can't restore: package no longer allows backup");
86            return false;
87        }
88        if (target.versionCode < mVersionCode) {
89            Slog.w(TAG, String.format(
90                    "Can't restore: package current version %d < backed up version %d",
91                    target.versionCode, mVersionCode));
92            return false;
93        }
94        if (!BackupUtils.signaturesMatch(mSigHashes, target)) {
95            Slog.w(TAG, "Can't restore: Package signature mismatch");
96            return false;
97        }
98        return true;
99    }
100
101    public static ShortcutPackageInfo generateForInstalledPackage(
102            ShortcutService s, String packageName, @UserIdInt int packageUserId) {
103        final PackageInfo pi = s.getPackageInfoWithSignatures(packageName, packageUserId);
104        if (pi.signatures == null || pi.signatures.length == 0) {
105            Slog.e(TAG, "Can't get signatures: package=" + packageName);
106            return null;
107        }
108        final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.versionCode,
109                BackupUtils.hashSignatureArray(pi.signatures), /* shadow=*/ false);
110
111        return ret;
112    }
113
114    public void refresh(ShortcutService s, ShortcutPackageItem pkg) {
115        if (mIsShadow) {
116            s.wtf("Attempted to refresh package info for shadow package " + pkg.getPackageName()
117                    + ", user=" + pkg.getOwnerUserId());
118            return;
119        }
120        // Note use mUserId here, rather than userId.
121        final PackageInfo pi = s.getPackageInfoWithSignatures(
122                pkg.getPackageName(), pkg.getPackageUserId());
123        if (pi == null) {
124            Slog.w(TAG, "Package not found: " + pkg.getPackageName());
125            return;
126        }
127        mVersionCode = pi.versionCode;
128        mSigHashes = BackupUtils.hashSignatureArray(pi.signatures);
129    }
130
131    public void saveToXml(XmlSerializer out) throws IOException {
132
133        out.startTag(null, TAG_ROOT);
134
135        ShortcutService.writeAttr(out, ATTR_VERSION, mVersionCode);
136        ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow);
137
138        for (int i = 0; i < mSigHashes.size(); i++) {
139            out.startTag(null, TAG_SIGNATURE);
140            ShortcutService.writeAttr(out, ATTR_SIGNATURE_HASH, Base64.encode(mSigHashes.get(i)));
141            out.endTag(null, TAG_SIGNATURE);
142        }
143        out.endTag(null, TAG_ROOT);
144    }
145
146    public void loadFromXml(XmlPullParser parser, boolean fromBackup)
147            throws IOException, XmlPullParserException {
148
149        final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION);
150
151        // When restoring from backup, it's always shadow.
152        final boolean shadow =
153                fromBackup || ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW);
154
155        final ArrayList<byte[]> hashes = new ArrayList<>();
156
157        final int outerDepth = parser.getDepth();
158        int type;
159        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
160                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
161            if (type != XmlPullParser.START_TAG) {
162                continue;
163            }
164            final int depth = parser.getDepth();
165            final String tag = parser.getName();
166
167            if (depth == outerDepth + 1) {
168                switch (tag) {
169                    case TAG_SIGNATURE: {
170                        final String hash = ShortcutService.parseStringAttribute(
171                                parser, ATTR_SIGNATURE_HASH);
172                        hashes.add(Base64.decode(hash.getBytes()));
173                        continue;
174                    }
175                }
176            }
177            ShortcutService.warnForInvalidTag(depth, tag);
178        }
179
180        // Successfully loaded; replace the feilds.
181        mVersionCode = versionCode;
182        mIsShadow = shadow;
183        mSigHashes = hashes;
184    }
185
186    public void dump(ShortcutService s, PrintWriter pw, String prefix) {
187        pw.println();
188
189        pw.print(prefix);
190        pw.println("PackageInfo:");
191
192        pw.print(prefix);
193        pw.print("  IsShadow: ");
194        pw.print(mIsShadow);
195        pw.println();
196
197        pw.print(prefix);
198        pw.print("  Version: ");
199        pw.print(mVersionCode);
200        pw.println();
201
202        for (int i = 0; i < mSigHashes.size(); i++) {
203            pw.print(prefix);
204            pw.print("    ");
205            pw.print("SigHash: ");
206            pw.println(HexEncoding.encode(mSigHashes.get(i)));
207        }
208    }
209}
210