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