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.util.HexEncoding;
     27 
     28 import org.xmlpull.v1.XmlPullParser;
     29 import org.xmlpull.v1.XmlPullParserException;
     30 import org.xmlpull.v1.XmlSerializer;
     31 
     32 import java.io.IOException;
     33 import java.io.PrintWriter;
     34 import java.util.ArrayList;
     35 import java.util.Base64;
     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             final String encoded = Base64.getEncoder().encodeToString(mSigHashes.get(i));
    165             ShortcutService.writeAttr(out, ATTR_SIGNATURE_HASH, encoded);
    166             out.endTag(null, TAG_SIGNATURE);
    167         }
    168         out.endTag(null, TAG_ROOT);
    169     }
    170 
    171     public void loadFromXml(XmlPullParser parser, boolean fromBackup)
    172             throws IOException, XmlPullParserException {
    173 
    174         final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION);
    175 
    176         final long lastUpdateTime = ShortcutService.parseLongAttribute(
    177                 parser, ATTR_LAST_UPDATE_TIME);
    178 
    179         // When restoring from backup, it's always shadow.
    180         final boolean shadow =
    181                 fromBackup || ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW);
    182 
    183         final ArrayList<byte[]> hashes = new ArrayList<>();
    184 
    185         final int outerDepth = parser.getDepth();
    186         int type;
    187         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
    188                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
    189             if (type != XmlPullParser.START_TAG) {
    190                 continue;
    191             }
    192             final int depth = parser.getDepth();
    193             final String tag = parser.getName();
    194 
    195             if (depth == outerDepth + 1) {
    196                 switch (tag) {
    197                     case TAG_SIGNATURE: {
    198                         final String hash = ShortcutService.parseStringAttribute(
    199                                 parser, ATTR_SIGNATURE_HASH);
    200                         // Throws IllegalArgumentException if hash is invalid base64 data
    201                         final byte[] decoded = Base64.getDecoder().decode(hash);
    202                         hashes.add(decoded);
    203                         continue;
    204                     }
    205                 }
    206             }
    207             ShortcutService.warnForInvalidTag(depth, tag);
    208         }
    209 
    210         // Successfully loaded; replace the feilds.
    211         mVersionCode = versionCode;
    212         mLastUpdateTime = lastUpdateTime;
    213         mIsShadow = shadow;
    214         mSigHashes = hashes;
    215     }
    216 
    217     public void dump(PrintWriter pw, String prefix) {
    218         pw.println();
    219 
    220         pw.print(prefix);
    221         pw.println("PackageInfo:");
    222 
    223         pw.print(prefix);
    224         pw.print("  IsShadow: ");
    225         pw.print(mIsShadow);
    226         pw.println();
    227 
    228         pw.print(prefix);
    229         pw.print("  Version: ");
    230         pw.print(mVersionCode);
    231         pw.println();
    232 
    233         pw.print(prefix);
    234         pw.print("  Last package update time: ");
    235         pw.print(mLastUpdateTime);
    236         pw.println();
    237 
    238         for (int i = 0; i < mSigHashes.size(); i++) {
    239             pw.print(prefix);
    240             pw.print("    ");
    241             pw.print("SigHash: ");
    242             pw.println(HexEncoding.encode(mSigHashes.get(i)));
    243         }
    244     }
    245 }
    246