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