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