1/* 2 * Copyright (C) 2017 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 17package com.android.server.backup.utils; 18 19import static com.android.server.backup.RefactoredBackupManagerService.MORE_DEBUG; 20import static com.android.server.backup.RefactoredBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE; 21import static com.android.server.backup.RefactoredBackupManagerService.TAG; 22 23import android.content.pm.ApplicationInfo; 24import android.content.pm.PackageInfo; 25import android.content.pm.Signature; 26import android.os.Process; 27import android.util.Slog; 28 29import com.android.internal.util.ArrayUtils; 30 31/** 32 * Utility methods wrapping operations on ApplicationInfo and PackageInfo. 33 */ 34public class AppBackupUtils { 35 /** 36 * Returns whether app is eligible for backup. 37 * 38 * High level policy: apps are generally ineligible for backup if certain conditions apply. The 39 * conditions are: 40 * 41 * <ol> 42 * <li>their manifest states android:allowBackup="false" 43 * <li>they run as a system-level uid but do not supply their own backup agent 44 * <li>it is the special shared-storage backup package used for 'adb backup' 45 * </ol> 46 */ 47 public static boolean appIsEligibleForBackup(ApplicationInfo app) { 48 // 1. their manifest states android:allowBackup="false" 49 if ((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 50 return false; 51 } 52 53 // 2. they run as a system-level uid but do not supply their own backup agent 54 if ((app.uid < Process.FIRST_APPLICATION_UID) && (app.backupAgentName == null)) { 55 return false; 56 } 57 58 // 3. it is the special shared-storage backup package used for 'adb backup' 59 if (app.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE)) { 60 return false; 61 } 62 63 return true; 64 } 65 66 /** 67 * Checks if the app is in a stopped state, that means it won't receive broadcasts. 68 */ 69 public static boolean appIsStopped(ApplicationInfo app) { 70 return ((app.flags & ApplicationInfo.FLAG_STOPPED) != 0); 71 } 72 73 /** 74 * Returns whether the app can get full backup. Does *not* check overall backup eligibility 75 * policy! 76 */ 77 public static boolean appGetsFullBackup(PackageInfo pkg) { 78 if (pkg.applicationInfo.backupAgentName != null) { 79 // If it has an agent, it gets full backups only if it says so 80 return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0; 81 } 82 83 // No agent or fullBackupOnly="true" means we do indeed perform full-data backups for it 84 return true; 85 } 86 87 /** 88 * Returns whether the app is only capable of doing key/value. We say it's not if it allows full 89 * backup, and it is otherwise. 90 */ 91 public static boolean appIsKeyValueOnly(PackageInfo pkg) { 92 return !appGetsFullBackup(pkg); 93 } 94 95 /** 96 * Returns whether the signatures stored {@param storedSigs}, coming from the source apk, match 97 * the signatures of the apk installed on the device, the target apk. If the target resides in 98 * the system partition we return true. Otherwise it's considered a match if both conditions 99 * hold: 100 * 101 * <ul> 102 * <li>Source and target have at least one signature each 103 * <li>Target contains all signatures in source 104 * </ul> 105 * 106 * Note that if {@param target} is null we return false. 107 */ 108 public static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) { 109 if (target == null) { 110 return false; 111 } 112 113 // If the target resides on the system partition, we allow it to restore 114 // data from the like-named package in a restore set even if the signatures 115 // do not match. (Unlike general applications, those flashed to the system 116 // partition will be signed with the device's platform certificate, so on 117 // different phones the same system app will have different signatures.) 118 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 119 if (MORE_DEBUG) { 120 Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 121 } 122 return true; 123 } 124 125 Signature[] deviceSigs = target.signatures; 126 if (MORE_DEBUG) { 127 Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs + " device=" + deviceSigs); 128 } 129 130 // Don't allow unsigned apps on either end 131 if (ArrayUtils.isEmpty(storedSigs) || ArrayUtils.isEmpty(deviceSigs)) { 132 return false; 133 } 134 135 // Signatures can be added over time, so the target-device apk needs to contain all the 136 // source-device apk signatures, but not necessarily the other way around. 137 int nStored = storedSigs.length; 138 int nDevice = deviceSigs.length; 139 140 for (int i = 0; i < nStored; i++) { 141 boolean match = false; 142 for (int j = 0; j < nDevice; j++) { 143 if (storedSigs[i].equals(deviceSigs[j])) { 144 match = true; 145 break; 146 } 147 } 148 if (!match) { 149 return false; 150 } 151 } 152 return true; 153 } 154} 155