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