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