ShortcutPackageInfo.java revision 22fcc68e6be0edaa98f3dacf79d580a5e5d50005
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.NonNull;
19import android.annotation.UserIdInt;
20import android.content.pm.PackageInfo;
21import android.util.Slog;
22
23import com.android.server.backup.BackupUtils;
24
25import libcore.io.Base64;
26import libcore.util.HexEncoding;
27
28import org.xmlpull.v1.XmlPullParser;
29import org.xmlpull.v1.XmlPullParserException;
30import org.xmlpull.v1.XmlSerializer;
31
32import java.io.IOException;
33import java.io.PrintWriter;
34import java.util.ArrayList;
35
36/**
37 * Package information used by {@link android.content.pm.ShortcutManager} for backup / restore.
38 *
39 * All methods should be guarded by {@code ShortcutService.mLock}.
40 */
41class ShortcutPackageInfo {
42    private static final String TAG = ShortcutService.TAG;
43
44    static final String TAG_ROOT = "package-info";
45    private static final String ATTR_VERSION = "version";
46    private static final String ATTR_LAST_UPDATE_TIME = "last_udpate_time";
47    private static final String ATTR_SHADOW = "shadow";
48
49    private static final String TAG_SIGNATURE = "signature";
50    private static final String ATTR_SIGNATURE_HASH = "hash";
51
52    private static final int VERSION_UNKNOWN = -1;
53
54    /**
55     * When true, this package information was restored from the previous device, and the app hasn't
56     * been installed yet.
57     */
58    private boolean mIsShadow;
59    private int mVersionCode = VERSION_UNKNOWN;
60    private long mLastUpdateTime;
61    private ArrayList<byte[]> mSigHashes;
62
63    private ShortcutPackageInfo(int versionCode, long lastUpdateTime,
64            ArrayList<byte[]> sigHashes, boolean isShadow) {
65        mVersionCode = versionCode;
66        mLastUpdateTime = lastUpdateTime;
67        mIsShadow = isShadow;
68        mSigHashes = sigHashes;
69    }
70
71    public static ShortcutPackageInfo newEmpty() {
72        return new ShortcutPackageInfo(VERSION_UNKNOWN, /* last update time =*/ 0,
73                new ArrayList<>(0), /* isShadow */ false);
74    }
75
76    public boolean isShadow() {
77        return mIsShadow;
78    }
79
80    public void setShadow(boolean shadow) {
81        mIsShadow = shadow;
82    }
83
84    public int getVersionCode() {
85        return mVersionCode;
86    }
87
88    public long getLastUpdateTime() {
89        return mLastUpdateTime;
90    }
91
92    public void updateVersionInfo(@NonNull PackageInfo pi) {
93        if (pi != null) {
94            mVersionCode = pi.versionCode;
95            mLastUpdateTime = pi.lastUpdateTime;
96        }
97    }
98
99    public boolean hasSignatures() {
100        return mSigHashes.size() > 0;
101    }
102
103    public boolean canRestoreTo(ShortcutService s, PackageInfo target) {
104        if (!s.shouldBackupApp(target)) {
105            // "allowBackup" was true when backed up, but now false.
106            Slog.w(TAG, "Can't restore: package no longer allows backup");
107            return false;
108        }
109        if (target.versionCode < mVersionCode) {
110            Slog.w(TAG, String.format(
111                    "Can't restore: package current version %d < backed up version %d",
112                    target.versionCode, mVersionCode));
113            return false;
114        }
115        if (!BackupUtils.signaturesMatch(mSigHashes, target)) {
116            Slog.w(TAG, "Can't restore: Package signature mismatch");
117            return false;
118        }
119        return true;
120    }
121
122    public static ShortcutPackageInfo generateForInstalledPackage(
123            ShortcutService s, String packageName, @UserIdInt int packageUserId) {
124        final PackageInfo pi = s.getPackageInfoWithSignatures(packageName, packageUserId);
125        if (pi.signatures == null || pi.signatures.length == 0) {
126            Slog.e(TAG, "Can't get signatures: package=" + packageName);
127            return null;
128        }
129        final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.versionCode, pi.lastUpdateTime,
130                BackupUtils.hashSignatureArray(pi.signatures), /* shadow=*/ false);
131
132        return ret;
133    }
134
135    public void refresh(ShortcutService s, ShortcutPackageItem pkg) {
136        if (mIsShadow) {
137            s.wtf("Attempted to refresh package info for shadow package " + pkg.getPackageName()
138                    + ", user=" + pkg.getOwnerUserId());
139            return;
140        }
141        // Note use mUserId here, rather than userId.
142        final PackageInfo pi = s.getPackageInfoWithSignatures(
143                pkg.getPackageName(), pkg.getPackageUserId());
144        if (pi == null) {
145            Slog.w(TAG, "Package not found: " + pkg.getPackageName());
146            return;
147        }
148        mVersionCode = pi.versionCode;
149        mLastUpdateTime = pi.lastUpdateTime;
150        mSigHashes = BackupUtils.hashSignatureArray(pi.signatures);
151    }
152
153    public void saveToXml(XmlSerializer out) throws IOException {
154
155        out.startTag(null, TAG_ROOT);
156
157        ShortcutService.writeAttr(out, ATTR_VERSION, mVersionCode);
158        ShortcutService.writeAttr(out, ATTR_LAST_UPDATE_TIME, mLastUpdateTime);
159        ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow);
160
161        for (int i = 0; i < mSigHashes.size(); i++) {
162            out.startTag(null, TAG_SIGNATURE);
163            ShortcutService.writeAttr(out, ATTR_SIGNATURE_HASH, Base64.encode(mSigHashes.get(i)));
164            out.endTag(null, TAG_SIGNATURE);
165        }
166        out.endTag(null, TAG_ROOT);
167    }
168
169    public void loadFromXml(XmlPullParser parser, boolean fromBackup)
170            throws IOException, XmlPullParserException {
171
172        final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION);
173
174        final long lastUpdateTime = ShortcutService.parseIntAttribute(
175                parser, ATTR_LAST_UPDATE_TIME);
176
177        // When restoring from backup, it's always shadow.
178        final boolean shadow =
179                fromBackup || ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW);
180
181        final ArrayList<byte[]> hashes = new ArrayList<>();
182
183        final int outerDepth = parser.getDepth();
184        int type;
185        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
186                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
187            if (type != XmlPullParser.START_TAG) {
188                continue;
189            }
190            final int depth = parser.getDepth();
191            final String tag = parser.getName();
192
193            if (depth == outerDepth + 1) {
194                switch (tag) {
195                    case TAG_SIGNATURE: {
196                        final String hash = ShortcutService.parseStringAttribute(
197                                parser, ATTR_SIGNATURE_HASH);
198                        hashes.add(Base64.decode(hash.getBytes()));
199                        continue;
200                    }
201                }
202            }
203            ShortcutService.warnForInvalidTag(depth, tag);
204        }
205
206        // Successfully loaded; replace the feilds.
207        mVersionCode = versionCode;
208        mLastUpdateTime = lastUpdateTime;
209        mIsShadow = shadow;
210        mSigHashes = hashes;
211    }
212
213    public void dump(PrintWriter pw, String prefix) {
214        pw.println();
215
216        pw.print(prefix);
217        pw.println("PackageInfo:");
218
219        pw.print(prefix);
220        pw.print("  IsShadow: ");
221        pw.print(mIsShadow);
222        pw.println();
223
224        pw.print(prefix);
225        pw.print("  Version: ");
226        pw.print(mVersionCode);
227        pw.println();
228
229        pw.print(prefix);
230        pw.print("  Last package update time: ");
231        pw.print(mLastUpdateTime);
232        pw.println();
233
234        for (int i = 0; i < mSigHashes.size(); i++) {
235            pw.print(prefix);
236            pw.print("    ");
237            pw.print("SigHash: ");
238            pw.println(HexEncoding.encode(mSigHashes.get(i)));
239        }
240    }
241}
242