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