• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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