• 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.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