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