Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e9fd1fa3 authored by Christopher Tate's avatar Christopher Tate
Browse files

Back up / restore runtime permission grants

Only user-originated grant actions are backed up/restored.  This
includes outright grants, one-time denials, and "never ask again"
type denials.

Bug 19870549

Change-Id: I78b4a8abb713dc5d74b93cb53217b212d57b26e4
parent 9e74b3b8
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -294,6 +294,8 @@ interface IPackageManager {
    void restoreDefaultApps(in byte[] backup, int userId);
    byte[] getIntentFilterVerificationBackup(int userId);
    void restoreIntentFilterVerification(in byte[] backup, int userId);
    byte[] getPermissionGrantBackup(int userId);
    void restorePermissionGrants(in byte[] backup, int userId);

    /**
     * Report the set of 'Home' activity candidates, plus (if any) which of them
+78 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.backup;

import android.app.AppGlobals;
import android.app.backup.BlobBackupHelper;
import android.content.pm.IPackageManager;
import android.os.UserHandle;
import android.util.Slog;

public class PermissionBackupHelper extends BlobBackupHelper {
    private static final String TAG = "PermissionBackup";
    private static final boolean DEBUG = false;

    // current schema of the backup state blob
    private static final int STATE_VERSION = 1;

    // key under which the permission-grant state blob is committed to backup
    private static final String KEY_PERMISSIONS = "permissions";

    public PermissionBackupHelper() {
        super(STATE_VERSION, KEY_PERMISSIONS);
    }

    @Override
    protected byte[] getBackupPayload(String key) {
        IPackageManager pm = AppGlobals.getPackageManager();
        if (DEBUG) {
            Slog.d(TAG, "Handling backup of " + key);
        }
        try {
            switch (key) {
                case KEY_PERMISSIONS:
                    return pm.getPermissionGrantBackup(UserHandle.USER_SYSTEM);

                default:
                    Slog.w(TAG, "Unexpected backup key " + key);
            }
        } catch (Exception e) {
            Slog.e(TAG, "Unable to store payload " + key);
        }
        return null;
    }

    @Override
    protected void applyRestoredPayload(String key, byte[] payload) {
        IPackageManager pm = AppGlobals.getPackageManager();
        if (DEBUG) {
            Slog.d(TAG, "Handling restore of " + key);
        }
        try {
            switch (key) {
                case KEY_PERMISSIONS:
                    pm.restorePermissionGrants(payload, UserHandle.USER_SYSTEM);
                    break;

                default:
                    Slog.w(TAG, "Unexpected restore key " + key);
            }
        } catch (Exception e) {
            Slog.w(TAG, "Unable to restore key " + key);
        }
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
    private static final String SYNC_SETTINGS_HELPER = "account_sync_settings";
    private static final String PREFERRED_HELPER = "preferred_activities";
    private static final String NOTIFICATION_HELPER = "notifications";
    private static final String PERMISSION_HELPER = "permissions";

    // These paths must match what the WallpaperManagerService uses.  The leaf *_FILENAME
    // are also used in the full-backup file format, so must not change unless steps are
@@ -94,6 +95,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
        addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this));
        addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper());
        addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this));
        addHelper(PERMISSION_HELPER, new PermissionBackupHelper());

        super.onBackup(oldState, data, newState);
    }
@@ -128,6 +130,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
        addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this));
        addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper());
        addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this));
        addHelper(PERMISSION_HELPER, new PermissionBackupHelper());

        try {
            super.onRestore(data, appVersionCode, newState);
+233 −9
Original line number Diff line number Diff line
@@ -76,7 +76,6 @@ import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDWR;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
@@ -227,6 +226,7 @@ import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.EventLogTags;
import com.android.server.FgThread;
import com.android.server.IntentResolver;
@@ -978,6 +978,30 @@ public class PackageManagerService extends IPackageManager.Stub {
    private static final String TAG_DEFAULT_APPS = "da";
    private static final String TAG_INTENT_FILTER_VERIFICATION = "iv";
    private static final String TAG_PERMISSION_BACKUP = "perm-grant-backup";
    private static final String TAG_ALL_GRANTS = "rt-grants";
    private static final String TAG_GRANT = "grant";
    private static final String ATTR_PACKAGE_NAME = "pkg";
    private static final String TAG_PERMISSION = "perm";
    private static final String ATTR_PERMISSION_NAME = "name";
    private static final String ATTR_IS_GRANTED = "g";
    private static final String ATTR_USER_SET = "set";
    private static final String ATTR_USER_FIXED = "fixed";
    private static final String ATTR_REVOKE_ON_UPGRADE = "rou";
    // System/policy permission grants are not backed up
    private static final int SYSTEM_RUNTIME_GRANT_MASK =
            FLAG_PERMISSION_POLICY_FIXED
            | FLAG_PERMISSION_SYSTEM_FIXED
            | FLAG_PERMISSION_GRANTED_BY_DEFAULT;
    // And we back up these user-adjusted states
    private static final int USER_RUNTIME_GRANT_MASK =
            FLAG_PERMISSION_USER_SET
            | FLAG_PERMISSION_USER_FIXED
            | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
    final @Nullable String mRequiredVerifierPackage;
    final @Nullable String mRequiredInstallerPackage;
@@ -1490,16 +1514,25 @@ public class PackageManagerService extends IPackageManager.Stub {
                                deleteOld = true;
                            }
                            // If this app is a browser and it's newly-installed for some
                            // users, clear any default-browser state in those users
                            // Work that needs to happen on first install within each user
                            if (firstUsers.length > 0) {
                                // the app's nature doesn't depend on the user, so we can just
                                // check its browser nature in any user and generalize.
                                if (packageIsBrowser(packageName, firstUsers[0])) {
                                    synchronized (mPackages) {
                                for (int userId : firstUsers) {
                                            mSettings.setDefaultBrowserPackageNameLPw(null, userId);
                                    synchronized (mPackages) {
                                        // If this app is a browser and it's newly-installed for
                                        // some users, clear any default-browser state in those
                                        // users.  The app's nature doesn't depend on the user,
                                        // so we can just check its browser nature in any user
                                        // and generalize.
                                        if (packageIsBrowser(packageName, firstUsers[0])) {
                                            mSettings.setDefaultBrowserPackageNameLPw(
                                                    null, userId);
                                        }
                                        // We may also need to apply pending (restored) runtime
                                        // permission grants within these users.
                                        mSettings.applyPendingPermissionGrantsLPw(
                                                packageName, userId);
                                    }
                                }
                            }
@@ -14823,7 +14856,7 @@ public class PackageManagerService extends IPackageManager.Stub {
            }
            return;
        }
Slog.v(TAG, ":: restoreFromXml() : got to tag " + parser.getName());
        // this is supposed to be TAG_PREFERRED_BACKUP
        if (!expectedStartTag.equals(parser.getName())) {
            if (DEBUG_BACKUP) {
@@ -14834,6 +14867,7 @@ public class PackageManagerService extends IPackageManager.Stub {
        // skip interfering stuff, then we're aligned with the backing implementation
        while ((type = parser.next()) == XmlPullParser.TEXT) { }
Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
        functor.apply(parser, userId);
    }
@@ -15020,6 +15054,192 @@ public class PackageManagerService extends IPackageManager.Stub {
        }
    }
    @Override
    public byte[] getPermissionGrantBackup(int userId) {
        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
            throw new SecurityException("Only the system may call getPermissionGrantBackup()");
        }
        ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
        try {
            final XmlSerializer serializer = new FastXmlSerializer();
            serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
            serializer.startDocument(null, true);
            serializer.startTag(null, TAG_PERMISSION_BACKUP);
            synchronized (mPackages) {
                serializeRuntimePermissionGrantsLPr(serializer, userId);
            }
            serializer.endTag(null, TAG_PERMISSION_BACKUP);
            serializer.endDocument();
            serializer.flush();
        } catch (Exception e) {
            if (DEBUG_BACKUP) {
                Slog.e(TAG, "Unable to write default apps for backup", e);
            }
            return null;
        }
        return dataStream.toByteArray();
    }
    @Override
    public void restorePermissionGrants(byte[] backup, int userId) {
        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
            throw new SecurityException("Only the system may call restorePermissionGrants()");
        }
        try {
            final XmlPullParser parser = Xml.newPullParser();
            parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
            restoreFromXml(parser, userId, TAG_PERMISSION_BACKUP,
                    new BlobXmlRestorer() {
                        @Override
                        public void apply(XmlPullParser parser, int userId)
                                throws XmlPullParserException, IOException {
                            synchronized (mPackages) {
                                processRestoredPermissionGrantsLPr(parser, userId);
                            }
                        }
                    } );
        } catch (Exception e) {
            if (DEBUG_BACKUP) {
                Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
            }
        }
    }
    private void serializeRuntimePermissionGrantsLPr(XmlSerializer serializer, final int userId)
            throws IOException {
        serializer.startTag(null, TAG_ALL_GRANTS);
        final int N = mSettings.mPackages.size();
        for (int i = 0; i < N; i++) {
            final PackageSetting ps = mSettings.mPackages.valueAt(i);
            boolean pkgGrantsKnown = false;
            PermissionsState packagePerms = ps.getPermissionsState();
            for (PermissionState state : packagePerms.getRuntimePermissionStates(userId)) {
                final int grantFlags = state.getFlags();
                // only look at grants that are not system/policy fixed
                if ((grantFlags & SYSTEM_RUNTIME_GRANT_MASK) == 0) {
                    final boolean isGranted = state.isGranted();
                    // And only back up the user-twiddled state bits
                    if (isGranted || (grantFlags & USER_RUNTIME_GRANT_MASK) != 0) {
                        final String packageName = mSettings.mPackages.keyAt(i);
                        if (!pkgGrantsKnown) {
                            serializer.startTag(null, TAG_GRANT);
                            serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
                            pkgGrantsKnown = true;
                        }
                        final boolean userSet =
                                (grantFlags & FLAG_PERMISSION_USER_SET) != 0;
                        final boolean userFixed =
                                (grantFlags & FLAG_PERMISSION_USER_FIXED) != 0;
                        final boolean revoke =
                                (grantFlags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0;
                        serializer.startTag(null, TAG_PERMISSION);
                        serializer.attribute(null, ATTR_PERMISSION_NAME, state.getName());
                        if (isGranted) {
                            serializer.attribute(null, ATTR_IS_GRANTED, "true");
                        }
                        if (userSet) {
                            serializer.attribute(null, ATTR_USER_SET, "true");
                        }
                        if (userFixed) {
                            serializer.attribute(null, ATTR_USER_FIXED, "true");
                        }
                        if (revoke) {
                            serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true");
                        }
                        serializer.endTag(null, TAG_PERMISSION);
                    }
                }
            }
            if (pkgGrantsKnown) {
                serializer.endTag(null, TAG_GRANT);
            }
        }
        serializer.endTag(null, TAG_ALL_GRANTS);
    }
    private void processRestoredPermissionGrantsLPr(XmlPullParser parser, int userId)
            throws XmlPullParserException, IOException {
        String pkgName = null;
        int outerDepth = parser.getDepth();
        int type;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }
            final String tagName = parser.getName();
            if (tagName.equals(TAG_GRANT)) {
                pkgName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
                if (DEBUG_BACKUP) {
                    Slog.v(TAG, "+++ Restoring grants for package " + pkgName);
                }
            } else if (tagName.equals(TAG_PERMISSION)) {
                final boolean isGranted = "true".equals(parser.getAttributeValue(null, ATTR_IS_GRANTED));
                final String permName = parser.getAttributeValue(null, ATTR_PERMISSION_NAME);
                int newFlagSet = 0;
                if ("true".equals(parser.getAttributeValue(null, ATTR_USER_SET))) {
                    newFlagSet |= FLAG_PERMISSION_USER_SET;
                }
                if ("true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED))) {
                    newFlagSet |= FLAG_PERMISSION_USER_FIXED;
                }
                if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) {
                    newFlagSet |= FLAG_PERMISSION_REVOKE_ON_UPGRADE;
                }
                if (DEBUG_BACKUP) {
                    Slog.v(TAG, "  + Restoring grant: pkg=" + pkgName + " perm=" + permName
                            + " granted=" + isGranted + " bits=0x" + Integer.toHexString(newFlagSet));
                }
                final PackageSetting ps = mSettings.mPackages.get(pkgName);
                if (ps != null) {
                    // Already installed so we apply the grant immediately
                    if (DEBUG_BACKUP) {
                        Slog.v(TAG, "        + already installed; applying");
                    }
                    PermissionsState perms = ps.getPermissionsState();
                    BasePermission bp = mSettings.mPermissions.get(permName);
                    if (bp != null) {
                        if (isGranted) {
                            perms.grantRuntimePermission(bp, userId);
                        }
                        if (newFlagSet != 0) {
                            perms.updatePermissionFlags(bp, userId, USER_RUNTIME_GRANT_MASK, newFlagSet);
                        }
                    }
                } else {
                    // Need to wait for post-restore install to apply the grant
                    if (DEBUG_BACKUP) {
                        Slog.v(TAG, "        - not yet installed; saving for later");
                    }
                    mSettings.processRestoredPermissionGrantLPr(pkgName, permName,
                            isGranted, newFlagSet, userId);
                }
            } else {
                PackageManagerService.reportSettingsProblem(Log.WARN,
                        "Unknown element under <" + TAG_PERMISSION_BACKUP + ">: " + tagName);
                XmlUtils.skipCurrentTag(parser);
            }
        }
        scheduleWriteSettingsLocked();
        mSettings.writeRuntimePermissionsForUserLPr(userId, false);
    }
    @Override
    public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage,
            int sourceUserId, int targetUserId, int flags) {
@@ -16032,6 +16252,10 @@ public class PackageManagerService extends IPackageManager.Stub {
                mSettings.dumpSharedUsersLPr(pw, packageName, permissionNames, dumpState, checkin);
            }
            if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS) && packageName == null) {
                mSettings.dumpRestoredPermissionGrantsLPr(pw, dumpState);
            }
            if (!checkin && dumpState.isDumping(DumpState.DUMP_INSTALLS) && packageName == null) {
                // XXX should handle packageName != null by dumping only install data that
                // the given package is involved with.
+238 −3

File changed.

Preview size limit exceeded, changes collapsed.