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 17 package com.android.server.backup.utils; 18 19 import static com.android.server.backup.BackupManagerService.MORE_DEBUG; 20 import static com.android.server.backup.BackupManagerService.TAG; 21 import static com.android.server.backup.UserBackupManagerService.PACKAGE_MANAGER_SENTINEL; 22 import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE; 23 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; 24 25 import android.annotation.Nullable; 26 import android.app.backup.BackupTransport; 27 import android.content.pm.ApplicationInfo; 28 import android.content.pm.PackageInfo; 29 import android.content.pm.PackageManager; 30 import android.content.pm.PackageManagerInternal; 31 import android.content.pm.Signature; 32 import android.content.pm.SigningInfo; 33 import android.os.UserHandle; 34 import android.util.Slog; 35 36 import com.android.internal.annotations.VisibleForTesting; 37 import com.android.internal.backup.IBackupTransport; 38 import com.android.internal.util.ArrayUtils; 39 import com.android.server.LocalServices; 40 import com.android.server.backup.transport.TransportClient; 41 42 import com.google.android.collect.Sets; 43 44 import java.util.Set; 45 46 /** 47 * Utility methods wrapping operations on ApplicationInfo and PackageInfo. 48 */ 49 public class AppBackupUtils { 50 private static final boolean DEBUG = false; 51 // Whitelist of system packages that are eligible for backup in non-system users. 52 private static final Set<String> systemPackagesWhitelistedForAllUsers = 53 Sets.newArraySet(PACKAGE_MANAGER_SENTINEL, PLATFORM_PACKAGE_NAME); 54 55 /** 56 * Returns whether app is eligible for backup. 57 * 58 * High level policy: apps are generally ineligible for backup if certain conditions apply. The 59 * conditions are: 60 * 61 * <ol> 62 * <li>their manifest states android:allowBackup="false" 63 * <li>they run as a system-level uid but do not supply their own backup agent 64 * <li>it is the special shared-storage backup package used for 'adb backup' 65 * </ol> 66 */ appIsEligibleForBackup(ApplicationInfo app, int userId)67 public static boolean appIsEligibleForBackup(ApplicationInfo app, int userId) { 68 return appIsEligibleForBackup( 69 app, LocalServices.getService(PackageManagerInternal.class), userId); 70 } 71 72 @VisibleForTesting appIsEligibleForBackup( ApplicationInfo app, PackageManagerInternal packageManager, int userId)73 static boolean appIsEligibleForBackup( 74 ApplicationInfo app, PackageManagerInternal packageManager, int userId) { 75 // 1. their manifest states android:allowBackup="false" 76 if ((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 77 return false; 78 } 79 80 // 2. they run as a system-level uid 81 if (UserHandle.isCore(app.uid)) { 82 // and the backup is happening for non-system user on a non-whitelisted package. 83 if (userId != UserHandle.USER_SYSTEM 84 && !systemPackagesWhitelistedForAllUsers.contains(app.packageName)) { 85 return false; 86 } 87 88 // or do not supply their own backup agent 89 if (app.backupAgentName == null) { 90 return false; 91 } 92 } 93 94 // 3. it is the special shared-storage backup package used for 'adb backup' 95 if (app.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE)) { 96 return false; 97 } 98 99 // 4. it is an "instant" app 100 if (app.isInstantApp()) { 101 return false; 102 } 103 104 return !appIsDisabled(app, packageManager, userId); 105 } 106 107 /** 108 * Returns whether an app is eligible for backup at runtime. That is, the app has to: 109 * <ol> 110 * <li>Return true for {@link #appIsEligibleForBackup(ApplicationInfo, int)} 111 * <li>Return false for {@link #appIsStopped(ApplicationInfo)} 112 * <li>Return false for {@link #appIsDisabled(ApplicationInfo, int)} 113 * <li>Be eligible for the transport via 114 * {@link BackupTransport#isAppEligibleForBackup(PackageInfo, boolean)} 115 * </ol> 116 */ appIsRunningAndEligibleForBackupWithTransport( @ullable TransportClient transportClient, String packageName, PackageManager pm, int userId)117 public static boolean appIsRunningAndEligibleForBackupWithTransport( 118 @Nullable TransportClient transportClient, 119 String packageName, 120 PackageManager pm, 121 int userId) { 122 try { 123 PackageInfo packageInfo = pm.getPackageInfoAsUser(packageName, 124 PackageManager.GET_SIGNING_CERTIFICATES, userId); 125 ApplicationInfo applicationInfo = packageInfo.applicationInfo; 126 if (!appIsEligibleForBackup(applicationInfo, userId) 127 || appIsStopped(applicationInfo) 128 || appIsDisabled(applicationInfo, userId)) { 129 return false; 130 } 131 if (transportClient != null) { 132 try { 133 IBackupTransport transport = 134 transportClient.connectOrThrow( 135 "AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport"); 136 return transport.isAppEligibleForBackup( 137 packageInfo, AppBackupUtils.appGetsFullBackup(packageInfo)); 138 } catch (Exception e) { 139 Slog.e(TAG, "Unable to ask about eligibility: " + e.getMessage()); 140 } 141 } 142 // If transport is not present we couldn't tell that the package is not eligible. 143 return true; 144 } catch (PackageManager.NameNotFoundException e) { 145 return false; 146 } 147 } 148 149 /** Avoid backups of 'disabled' apps. */ appIsDisabled(ApplicationInfo app, int userId)150 static boolean appIsDisabled(ApplicationInfo app, int userId) { 151 return appIsDisabled(app, LocalServices.getService(PackageManagerInternal.class), userId); 152 } 153 154 @VisibleForTesting appIsDisabled( ApplicationInfo app, PackageManagerInternal packageManager, int userId)155 static boolean appIsDisabled( 156 ApplicationInfo app, PackageManagerInternal packageManager, int userId) { 157 int enabledSetting = packageManager.getApplicationEnabledState(app.packageName, userId); 158 159 switch (enabledSetting) { 160 case PackageManager.COMPONENT_ENABLED_STATE_DISABLED: 161 case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER: 162 case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED: 163 return true; 164 case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT: 165 return !app.enabled; 166 default: 167 return false; 168 } 169 } 170 171 /** 172 * Checks if the app is in a stopped state. This is not part of the general "eligible for 173 * backup?" check because we *do* still need to restore data to apps in this state (e.g. 174 * newly-installing ones). 175 * 176 * <p>Reasons for such state: 177 * <ul> 178 * <li>The app has been force-stopped. 179 * <li>The app has been cleared. 180 * <li>The app has just been installed. 181 * </ul> 182 */ appIsStopped(ApplicationInfo app)183 public static boolean appIsStopped(ApplicationInfo app) { 184 return ((app.flags & ApplicationInfo.FLAG_STOPPED) != 0); 185 } 186 187 /** 188 * Returns whether the app can get full backup. Does *not* check overall backup eligibility 189 * policy! 190 */ appGetsFullBackup(PackageInfo pkg)191 public static boolean appGetsFullBackup(PackageInfo pkg) { 192 if (pkg.applicationInfo.backupAgentName != null) { 193 // If it has an agent, it gets full backups only if it says so 194 return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0; 195 } 196 197 // No agent or fullBackupOnly="true" means we do indeed perform full-data backups for it 198 return true; 199 } 200 201 /** 202 * Returns whether the app is only capable of doing key/value. We say it's not if it allows full 203 * backup, and it is otherwise. 204 */ appIsKeyValueOnly(PackageInfo pkg)205 public static boolean appIsKeyValueOnly(PackageInfo pkg) { 206 return !appGetsFullBackup(pkg); 207 } 208 209 /** 210 * Returns whether the signatures stored {@param storedSigs}, coming from the source apk, match 211 * the signatures of the apk installed on the device, the target apk. If the target resides in 212 * the system partition we return true. Otherwise it's considered a match if both conditions 213 * hold: 214 * 215 * <ul> 216 * <li>Source and target have at least one signature each 217 * <li>Target contains all signatures in source, and nothing more 218 * </ul> 219 * 220 * or if both source and target have exactly one signature, and they don't match, we check 221 * if the app was ever signed with source signature (i.e. app has rotated key) 222 * Note: key rotation is only supported for apps ever signed with one key, and those apps will 223 * not be allowed to be signed by more certificates in the future 224 * 225 * Note that if {@param target} is null we return false. 226 */ signaturesMatch(Signature[] storedSigs, PackageInfo target, PackageManagerInternal pmi)227 public static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target, 228 PackageManagerInternal pmi) { 229 if (target == null || target.packageName == null) { 230 return false; 231 } 232 233 // If the target resides on the system partition, we allow it to restore 234 // data from the like-named package in a restore set even if the signatures 235 // do not match. (Unlike general applications, those flashed to the system 236 // partition will be signed with the device's platform certificate, so on 237 // different phones the same system app will have different signatures.) 238 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 239 if (MORE_DEBUG) { 240 Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 241 } 242 return true; 243 } 244 245 // Don't allow unsigned apps on either end 246 if (ArrayUtils.isEmpty(storedSigs)) { 247 return false; 248 } 249 250 SigningInfo signingInfo = target.signingInfo; 251 if (signingInfo == null) { 252 Slog.w(TAG, "signingInfo is empty, app was either unsigned or the flag" + 253 " PackageManager#GET_SIGNING_CERTIFICATES was not specified"); 254 return false; 255 } 256 257 if (DEBUG) { 258 Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs + " device=" 259 + signingInfo.getApkContentsSigners()); 260 } 261 262 final int nStored = storedSigs.length; 263 if (nStored == 1) { 264 // if the app is only signed with one sig, it's possible it has rotated its key 265 // (the checks with signing history are delegated to PackageManager) 266 // TODO(b/73988180): address the case that app has declared restoreAnyVersion and is 267 // restoring from higher version to lower after having rotated the key (i.e. higher 268 // version has different sig than lower version that we want to restore to) 269 return pmi.isDataRestoreSafe(storedSigs[0], target.packageName); 270 } else { 271 // the app couldn't have rotated keys, since it was signed with multiple sigs - do 272 // a check to see if we find a match for all stored sigs 273 // since app hasn't rotated key, we only need to check with its current signers 274 Signature[] deviceSigs = signingInfo.getApkContentsSigners(); 275 int nDevice = deviceSigs.length; 276 277 // ensure that each stored sig matches an on-device sig 278 for (int i = 0; i < nStored; i++) { 279 boolean match = false; 280 for (int j = 0; j < nDevice; j++) { 281 if (storedSigs[i].equals(deviceSigs[j])) { 282 match = true; 283 break; 284 } 285 } 286 if (!match) { 287 return false; 288 } 289 } 290 // we have found a match for all stored sigs 291 return true; 292 } 293 } 294 } 295