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.BackupManager.OperationType; 27 import android.app.backup.BackupTransport; 28 import android.compat.annotation.ChangeId; 29 import android.compat.annotation.EnabledSince; 30 import android.app.compat.CompatChanges; 31 import android.compat.annotation.Overridable; 32 import android.content.pm.ApplicationInfo; 33 import android.content.pm.PackageInfo; 34 import android.content.pm.PackageManager; 35 import android.content.pm.PackageManagerInternal; 36 import android.content.pm.Signature; 37 import android.content.pm.SigningInfo; 38 import android.os.Build; 39 import android.os.UserHandle; 40 import android.util.Slog; 41 42 import com.android.internal.annotations.VisibleForTesting; 43 import com.android.internal.backup.IBackupTransport; 44 import com.android.internal.util.ArrayUtils; 45 import com.android.server.backup.transport.TransportClient; 46 47 import com.google.android.collect.Sets; 48 49 import java.util.Objects; 50 import java.util.Set; 51 52 /** 53 * Utility methods wrapping operations on ApplicationInfo and PackageInfo. 54 */ 55 public class BackupEligibilityRules { 56 private static final boolean DEBUG = false; 57 // List of system packages that are eligible for backup in non-system users. 58 private static final Set<String> systemPackagesAllowedForAllUsers = 59 Sets.newArraySet(PACKAGE_MANAGER_SENTINEL, PLATFORM_PACKAGE_NAME); 60 61 private final PackageManager mPackageManager; 62 private final PackageManagerInternal mPackageManagerInternal; 63 private final int mUserId; 64 @OperationType private final int mOperationType; 65 66 /** 67 * When this change is enabled, {@code adb backup} is automatically turned on for apps 68 * running as debuggable ({@code android:debuggable} set to {@code true}) and unavailable to 69 * any other apps. 70 */ 71 @ChangeId 72 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) 73 static final long RESTRICT_ADB_BACKUP = 171032338L; 74 75 /** 76 * When this change is enabled, {@code android:allowBackup} is ignored for apps during D2D 77 * (device-to-device) migrations. 78 */ 79 @ChangeId 80 @Overridable 81 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) 82 static final long IGNORE_ALLOW_BACKUP_IN_D2D = 183147249L; 83 forBackup(PackageManager packageManager, PackageManagerInternal packageManagerInternal, int userId)84 public static BackupEligibilityRules forBackup(PackageManager packageManager, 85 PackageManagerInternal packageManagerInternal, 86 int userId) { 87 return new BackupEligibilityRules(packageManager, packageManagerInternal, userId, 88 OperationType.BACKUP); 89 } 90 BackupEligibilityRules(PackageManager packageManager, PackageManagerInternal packageManagerInternal, int userId, @OperationType int operationType)91 public BackupEligibilityRules(PackageManager packageManager, 92 PackageManagerInternal packageManagerInternal, 93 int userId, 94 @OperationType int operationType) { 95 mPackageManager = packageManager; 96 mPackageManagerInternal = packageManagerInternal; 97 mUserId = userId; 98 mOperationType = operationType; 99 } 100 101 /** 102 * Returns whether app is eligible for backup. 103 * 104 * High level policy: apps are generally ineligible for backup if certain conditions apply. The 105 * conditions are: 106 * 107 * <ol> 108 * <li>their manifest states android:allowBackup="false" 109 * <li>they run as a system-level uid but do not supply their own backup agent 110 * <li>it is the special shared-storage backup package used for 'adb backup' 111 * </ol> 112 * 113 * However, the above eligibility rules are ignored for non-system apps in in case of 114 * device-to-device migration, see {@link OperationType}. 115 */ 116 @VisibleForTesting appIsEligibleForBackup(ApplicationInfo app)117 public boolean appIsEligibleForBackup(ApplicationInfo app) { 118 // 1. their manifest states android:allowBackup="false" and this is not a device-to-device 119 // migration 120 if (!isAppBackupAllowed(app)) { 121 return false; 122 } 123 124 // 2. they run as a system-level uid 125 if (UserHandle.isCore(app.uid)) { 126 // and the backup is happening for a non-system user on a package that is not explicitly 127 // allowed. 128 if (mUserId != UserHandle.USER_SYSTEM 129 && !systemPackagesAllowedForAllUsers.contains(app.packageName)) { 130 return false; 131 } 132 133 // or do not supply their own backup agent 134 if (app.backupAgentName == null) { 135 return false; 136 } 137 } 138 139 // 3. it is the special shared-storage backup package used for 'adb backup' 140 if (app.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE)) { 141 return false; 142 } 143 144 // 4. it is an "instant" app 145 if (app.isInstantApp()) { 146 return false; 147 } 148 149 return !appIsDisabled(app); 150 } 151 152 /** 153 * Check if this app allows backup. Apps can opt out of backup by stating 154 * android:allowBackup="false" in their manifest. However, this flag is ignored for non-system 155 * apps during device-to-device migrations, see {@link OperationType}. 156 * 157 * @param app The app under check. 158 * @return boolean indicating whether backup is allowed. 159 */ isAppBackupAllowed(ApplicationInfo app)160 public boolean isAppBackupAllowed(ApplicationInfo app) { 161 boolean allowBackup = (app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0; 162 switch (mOperationType) { 163 case OperationType.MIGRATION: 164 // Backup / restore of all non-system apps is force allowed during 165 // device-to-device migration. 166 boolean isSystemApp = (app.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 167 boolean ignoreAllowBackup = !isSystemApp && CompatChanges.isChangeEnabled( 168 IGNORE_ALLOW_BACKUP_IN_D2D, app.packageName, UserHandle.of(mUserId)); 169 return ignoreAllowBackup || allowBackup; 170 case OperationType.ADB_BACKUP: 171 String packageName = app.packageName; 172 if (packageName == null) { 173 Slog.w(TAG, "Invalid ApplicationInfo object"); 174 return false; 175 } 176 177 if (!CompatChanges.isChangeEnabled(RESTRICT_ADB_BACKUP, packageName, 178 UserHandle.of(mUserId))) { 179 return allowBackup; 180 } 181 182 if (PLATFORM_PACKAGE_NAME.equals(packageName)) { 183 // Always enable adb backup for SystemBackupAgent in "android" package (this is 184 // done to avoid breaking existing integration tests and might change in the 185 // future). 186 return true; 187 } 188 189 boolean isPrivileged = (app.flags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; 190 boolean isDebuggable = (app.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; 191 if (UserHandle.isCore(app.uid) || isPrivileged) { 192 try { 193 return mPackageManager.getProperty(PackageManager.PROPERTY_ALLOW_ADB_BACKUP, 194 packageName).getBoolean(); 195 } catch (PackageManager.NameNotFoundException e) { 196 Slog.w(TAG, "Failed to read allowAdbBackup property for + " 197 + packageName); 198 199 // This temporarily falls back to the legacy allowBackup flag to 200 // avoid breaking existing users of adb backup. Once they're able to use 201 // the new ALLOW_ADB_BACKUP property, we'll return false here. 202 // TODO(b/176088499): Return false here. 203 return allowBackup; 204 } 205 } else { 206 // All other apps can use adb backup only when running in debuggable mode. 207 return isDebuggable; 208 } 209 case OperationType.BACKUP: 210 return allowBackup; 211 default: 212 Slog.w(TAG, "Unknown operation type:" + mOperationType); 213 return false; 214 } 215 } 216 217 /** 218 * Returns whether an app is eligible for backup at runtime. That is, the app has to: 219 * <ol> 220 * <li>Return true for {@link #appIsEligibleForBackup(ApplicationInfo, int)} 221 * <li>Return false for {@link #appIsStopped(ApplicationInfo)} 222 * <li>Return false for {@link #appIsDisabled(ApplicationInfo, int)} 223 * <li>Be eligible for the transport via 224 * {@link BackupTransport#isAppEligibleForBackup(PackageInfo, boolean)} 225 * </ol> 226 */ appIsRunningAndEligibleForBackupWithTransport( @ullable TransportClient transportClient, String packageName)227 public boolean appIsRunningAndEligibleForBackupWithTransport( 228 @Nullable TransportClient transportClient, 229 String packageName) { 230 try { 231 PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageName, 232 PackageManager.GET_SIGNING_CERTIFICATES, mUserId); 233 ApplicationInfo applicationInfo = packageInfo.applicationInfo; 234 if (!appIsEligibleForBackup(applicationInfo) 235 || appIsStopped(applicationInfo) 236 || appIsDisabled(applicationInfo)) { 237 return false; 238 } 239 if (transportClient != null) { 240 try { 241 IBackupTransport transport = 242 transportClient.connectOrThrow( 243 "AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport"); 244 return transport.isAppEligibleForBackup( 245 packageInfo, appGetsFullBackup(packageInfo)); 246 } catch (Exception e) { 247 Slog.e(TAG, "Unable to ask about eligibility: " + e.getMessage()); 248 } 249 } 250 // If transport is not present we couldn't tell that the package is not eligible. 251 return true; 252 } catch (PackageManager.NameNotFoundException e) { 253 return false; 254 } 255 } 256 257 /** Avoid backups of 'disabled' apps. */ 258 @VisibleForTesting appIsDisabled( ApplicationInfo app)259 boolean appIsDisabled( 260 ApplicationInfo app) { 261 int enabledSetting = mPackageManagerInternal.getApplicationEnabledState(app.packageName, 262 mUserId); 263 264 switch (enabledSetting) { 265 case PackageManager.COMPONENT_ENABLED_STATE_DISABLED: 266 case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER: 267 case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED: 268 return true; 269 case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT: 270 return !app.enabled; 271 default: 272 return false; 273 } 274 } 275 276 /** 277 * Checks if the app is in a stopped state. This is not part of the general "eligible for 278 * backup?" check because we *do* still need to restore data to apps in this state (e.g. 279 * newly-installing ones). 280 * 281 * <p>Reasons for such state: 282 * <ul> 283 * <li>The app has been force-stopped. 284 * <li>The app has been cleared. 285 * <li>The app has just been installed. 286 * </ul> 287 */ appIsStopped(ApplicationInfo app)288 public boolean appIsStopped(ApplicationInfo app) { 289 return ((app.flags & ApplicationInfo.FLAG_STOPPED) != 0); 290 } 291 292 /** 293 * Returns whether the app can get full backup. Does *not* check overall backup eligibility 294 * policy! 295 */ 296 @VisibleForTesting appGetsFullBackup(PackageInfo pkg)297 public boolean appGetsFullBackup(PackageInfo pkg) { 298 if (pkg.applicationInfo.backupAgentName != null) { 299 // If it has an agent, it gets full backups only if it says so 300 return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0; 301 } 302 303 // No agent or fullBackupOnly="true" means we do indeed perform full-data backups for it 304 return true; 305 } 306 307 /** 308 * Returns whether the app is only capable of doing key/value. We say it's not if it allows full 309 * backup, and it is otherwise. 310 */ appIsKeyValueOnly(PackageInfo pkg)311 public boolean appIsKeyValueOnly(PackageInfo pkg) { 312 return !appGetsFullBackup(pkg); 313 } 314 315 /** 316 * Returns whether the signatures stored {@param storedSigs}, coming from the source apk, match 317 * the signatures of the apk installed on the device, the target apk. If the target resides in 318 * the system partition we return true. Otherwise it's considered a match if both conditions 319 * hold: 320 * 321 * <ul> 322 * <li>Source and target have at least one signature each 323 * <li>Target contains all signatures in source, and nothing more 324 * </ul> 325 * 326 * or if both source and target have exactly one signature, and they don't match, we check 327 * if the app was ever signed with source signature (i.e. app has rotated key) 328 * Note: key rotation is only supported for apps ever signed with one key, and those apps will 329 * not be allowed to be signed by more certificates in the future 330 * 331 * Note that if {@param target} is null we return false. 332 */ signaturesMatch(Signature[] storedSigs, PackageInfo target)333 public boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) { 334 if (target == null || target.packageName == null) { 335 return false; 336 } 337 338 // If the target resides on the system partition, we allow it to restore 339 // data from the like-named package in a restore set even if the signatures 340 // do not match. (Unlike general applications, those flashed to the system 341 // partition will be signed with the device's platform certificate, so on 342 // different phones the same system app will have different signatures.) 343 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 344 if (MORE_DEBUG) { 345 Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 346 } 347 return true; 348 } 349 350 // Don't allow unsigned apps on either end 351 if (ArrayUtils.isEmpty(storedSigs)) { 352 return false; 353 } 354 355 SigningInfo signingInfo = target.signingInfo; 356 if (signingInfo == null) { 357 Slog.w(TAG, "signingInfo is empty, app was either unsigned or the flag" + 358 " PackageManager#GET_SIGNING_CERTIFICATES was not specified"); 359 return false; 360 } 361 362 if (DEBUG) { 363 Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs + " device=" 364 + signingInfo.getApkContentsSigners()); 365 } 366 367 final int nStored = storedSigs.length; 368 if (nStored == 1) { 369 // if the app is only signed with one sig, it's possible it has rotated its key 370 // (the checks with signing history are delegated to PackageManager) 371 // TODO(b/73988180): address the case that app has declared restoreAnyVersion and is 372 // restoring from higher version to lower after having rotated the key (i.e. higher 373 // version has different sig than lower version that we want to restore to) 374 return mPackageManagerInternal.isDataRestoreSafe(storedSigs[0], target.packageName); 375 } else { 376 // the app couldn't have rotated keys, since it was signed with multiple sigs - do 377 // a check to see if we find a match for all stored sigs 378 // since app hasn't rotated key, we only need to check with its current signers 379 Signature[] deviceSigs = signingInfo.getApkContentsSigners(); 380 int nDevice = deviceSigs.length; 381 382 // ensure that each stored sig matches an on-device sig 383 for (int i = 0; i < nStored; i++) { 384 boolean match = false; 385 for (int j = 0; j < nDevice; j++) { 386 if (storedSigs[i].equals(deviceSigs[j])) { 387 match = true; 388 break; 389 } 390 } 391 if (!match) { 392 return false; 393 } 394 } 395 // we have found a match for all stored sigs 396 return true; 397 } 398 } 399 getOperationType()400 public int getOperationType() { 401 return mOperationType; 402 } 403 } 404