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

Commit ddb8a96f authored by Jason Monk's avatar Jason Monk
Browse files

Hook up the full slice access checkbox

Store the apps that have this access in xml

Test: uiservicestests
Bug: 68751119
Change-Id: I06a6eab17dbda1a5849fb553b4f27df1c1796403
parent 3a1d2e97
Loading
Loading
Loading
Loading
+131 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.slice;

import android.content.Context;
import android.content.pm.UserInfo;
import android.os.UserManager;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;

import com.android.internal.util.XmlUtils;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import java.io.IOException;
import java.util.List;

public class SliceFullAccessList {

    static final int DB_VERSION = 1;
    private static final String TAG = "SliceFullAccessList";

    private static final String TAG_LIST = "slice-access-list";
    private static final String TAG_PKG = "pkg";
    private static final String TAG_USER = "user";

    private final String ATT_USER_ID = "user";
    private final String ATT_VERSION = "version";

    private final SparseArray<ArraySet<String>> mFullAccessPkgs = new SparseArray<>();
    private final Context mContext;

    public SliceFullAccessList(Context context) {
        mContext = context;
    }

    public boolean hasFullAccess(String pkg, int userId) {
        ArraySet<String> pkgs = mFullAccessPkgs.get(userId, null);
        return pkgs != null && pkgs.contains(pkg);
    }

    public void grantFullAccess(String pkg, int userId) {
        ArraySet<String> pkgs = mFullAccessPkgs.get(userId, null);
        if (pkgs == null) {
            pkgs = new ArraySet<>();
            mFullAccessPkgs.put(userId, pkgs);
        }
        pkgs.add(pkg);
    }

    public void writeXml(XmlSerializer out) throws IOException {
        out.startTag(null, TAG_LIST);
        out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION));

        final int N = mFullAccessPkgs.size();
        for (int i = 0 ; i < N; i++) {
            final int userId = mFullAccessPkgs.keyAt(i);
            final ArraySet<String> pkgs = mFullAccessPkgs.valueAt(i);
            out.startTag(null, TAG_USER);
            out.attribute(null, ATT_USER_ID, Integer.toString(userId));
            if (pkgs != null) {
                final int M = pkgs.size();
                for (int j = 0; j < M; j++) {
                        out.startTag(null, TAG_PKG);
                        out.text(pkgs.valueAt(j));
                        out.endTag(null, TAG_PKG);

                }
            }
            out.endTag(null, TAG_USER);
        }
        out.endTag(null, TAG_LIST);
    }

    public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException {
        // upgrade xml
        int xmlVersion = XmlUtils.readIntAttribute(parser, ATT_VERSION, 0);
        final List<UserInfo> activeUsers = UserManager.get(mContext).getUsers(true);
        for (UserInfo userInfo : activeUsers) {
            upgradeXml(xmlVersion, userInfo.getUserHandle().getIdentifier());
        }

        mFullAccessPkgs.clear();
        // read grants
        int type;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
            String tag = parser.getName();
            if (type == XmlPullParser.END_TAG
                    && TAG_LIST.equals(tag)) {
                break;
            }
            if (type == XmlPullParser.START_TAG) {
                if (TAG_USER.equals(tag)) {
                    final int userId = XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0);
                    ArraySet<String> pkgs = new ArraySet<>();
                    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                        String userTag = parser.getName();
                        if (type == XmlPullParser.END_TAG
                                && TAG_USER.equals(userTag)) {
                            break;
                        }
                        if (type == XmlPullParser.START_TAG) {
                            if (TAG_PKG.equals(userTag)) {
                                final String pkg = parser.nextText();
                                pkgs.add(pkg);
                            }
                        }
                    }
                    mFullAccessPkgs.put(userId, pkgs);
                }
            }
        }
    }

    protected void upgradeXml(final int xmlVersion, final int userId) {}
}
+76 −15
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -47,7 +48,10 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
import android.util.Xml.Encoding;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -57,6 +61,16 @@ import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemService;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -77,6 +91,8 @@ public class SliceManagerService extends ISliceManager.Stub {
    private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>();
    private final Handler mHandler;
    private final ContentObserver mObserver;
    private final AtomicFile mSliceAccessFile;
    private final SliceFullAccessList mAccessList;

    public SliceManagerService(Context context) {
        this(context, createHandler().getLooper());
@@ -101,6 +117,21 @@ public class SliceManagerService extends ISliceManager.Stub {
                }
            }
        };
        final File systemDir = new File(Environment.getDataDirectory(), "system");
        mSliceAccessFile = new AtomicFile(new File(systemDir, "slice_access.xml"));
        mAccessList = new SliceFullAccessList(mContext);

        synchronized (mSliceAccessFile) {
            if (!mSliceAccessFile.exists()) return;
            try {
                InputStream input = mSliceAccessFile.openRead();
                XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
                parser.setInput(input, Encoding.UTF_8.name());
                mAccessList.readXml(parser);
            } catch (IOException | XmlPullParserException e) {
                Slog.d(TAG, "Can't read slice access file", e);
            }
        }
    }

    ///  ----- Lifecycle stuff -----
@@ -175,11 +206,11 @@ public class SliceManagerService extends ISliceManager.Stub {
                == PERMISSION_GRANTED) {
            return SliceManager.PERMISSION_GRANTED;
        }
        if (hasFullSliceAccess(pkg, uid)) {
        if (hasFullSliceAccess(pkg, UserHandle.getUserId(uid))) {
            return SliceManager.PERMISSION_GRANTED;
        }
        synchronized (mLock) {
            if (mUserGrants.contains(new SliceGrant(uri, pkg))) {
            if (mUserGrants.contains(new SliceGrant(uri, pkg, UserHandle.getUserId(uid)))) {
                return SliceManager.PERMISSION_USER_GRANTED;
            }
        }
@@ -192,10 +223,13 @@ public class SliceManagerService extends ISliceManager.Stub {
        getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS,
                "Slice granting requires MANAGE_SLICE_PERMISSIONS");
        if (allSlices) {
            // TODO: Manage full access grants.
            mAccessList.grantFullAccess(pkg, Binder.getCallingUserHandle().getIdentifier());
            mHandler.post(mSaveAccessList);
        } else {
            synchronized (mLock) {
                mUserGrants.add(new SliceGrant(uri, pkg));
                mUserGrants.add(new SliceGrant(uri, pkg,
                        Binder.getCallingUserHandle().getIdentifier()));
            }
        }
        long ident = Binder.clearCallingIdentity();
        try {
@@ -203,7 +237,6 @@ public class SliceManagerService extends ISliceManager.Stub {
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        }
        synchronized (mLock) {
            for (PinnedSliceState p : mPinnedSlicesByUri.values()) {
                p.recheckPackage(pkg);
@@ -260,7 +293,7 @@ public class SliceManagerService extends ISliceManager.Stub {
    protected int checkAccess(String pkg, Uri uri, int uid, int pid) {
        int user = UserHandle.getUserId(uid);
        // Check for default launcher/assistant.
        if (!hasFullSliceAccess(pkg, uid)) {
        if (!hasFullSliceAccess(pkg, user)) {
            // Also allow things with uri access.
            if (getContext().checkUriPermission(uri, pid, uid,
                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != PERMISSION_GRANTED) {
@@ -411,8 +444,7 @@ public class SliceManagerService extends ISliceManager.Stub {
    }

    private boolean isGrantedFullAccess(String pkg, int userId) {
        // TODO: This will be user granted access, if we allow this through a prompt.
        return false;
        return mAccessList.hasFullAccess(pkg, userId);
    }

    private static ServiceThread createHandler() {
@@ -422,6 +454,32 @@ public class SliceManagerService extends ISliceManager.Stub {
        return handlerThread;
    }

    private final Runnable mSaveAccessList = new Runnable() {
        @Override
        public void run() {
            synchronized (mSliceAccessFile) {
                final FileOutputStream stream;
                try {
                    stream = mSliceAccessFile.startWrite();
                } catch (IOException e) {
                    Slog.w(TAG, "Failed to save access file", e);
                    return;
                }

                try {
                    XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
                    out.setOutput(stream, Encoding.UTF_8.name());
                    mAccessList.writeXml(out);
                    out.flush();
                    mSliceAccessFile.finishWrite(stream);
                } catch (IOException | XmlPullParserException e) {
                    Slog.w(TAG, "Failed to save access file, restoring backup", e);
                    mSliceAccessFile.failWrite(stream);
                }
            }
        }
    };

    public static class Lifecycle extends SystemService {
        private SliceManagerService mService;

@@ -456,10 +514,12 @@ public class SliceManagerService extends ISliceManager.Stub {
    private class SliceGrant {
        private final Uri mUri;
        private final String mPkg;
        private final int mUserId;

        public SliceGrant(Uri uri, String pkg) {
        public SliceGrant(Uri uri, String pkg, int userId) {
            mUri = uri;
            mPkg = pkg;
            mUserId = userId;
        }

        @Override
@@ -471,7 +531,8 @@ public class SliceManagerService extends ISliceManager.Stub {
        public boolean equals(Object obj) {
            if (!(obj instanceof SliceGrant)) return false;
            SliceGrant other = (SliceGrant) obj;
            return Objects.equals(other.mUri, mUri) && Objects.equals(other.mPkg, mPkg);
            return Objects.equals(other.mUri, mUri) && Objects.equals(other.mPkg, mPkg)
                    && (other.mUserId == mUserId);
        }
    }
}
+103 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.slice;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import android.support.test.filters.SmallTest;
import android.util.Xml.Encoding;

import com.android.server.UiServiceTestCase;

import org.junit.Before;
import org.junit.Test;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

@SmallTest
public class SliceFullAccessListTest extends UiServiceTestCase {

    private static final String TEST_XML = "<slice-access-list version=\"1\"><user "
            + "user=\"0\"><pkg>pkg</pkg><pkg>pkg1</pkg></user><user "
            + "user=\"1\"><pkg>pkg</pkg></user><user "
            + "user=\"3\"><pkg>pkg2</pkg></user></slice-access-list>";

    private SliceFullAccessList mAccessList;

    @Before
    public void setup() {
        mAccessList = new SliceFullAccessList(mContext);
    }

    @Test
    public void testNoDefaultAccess() {
        assertFalse(mAccessList.hasFullAccess("pkg", 0));
    }

    @Test
    public void testGrantAccess() {
        mAccessList.grantFullAccess("pkg", 0);
        assertTrue(mAccessList.hasFullAccess("pkg", 0));
    }

    @Test
    public void testUserSeparation() {
        mAccessList.grantFullAccess("pkg", 1);
        assertFalse(mAccessList.hasFullAccess("pkg", 0));
    }

    @Test
    public void testSerialization() throws XmlPullParserException, IOException {
        mAccessList.grantFullAccess("pkg", 0);
        mAccessList.grantFullAccess("pkg1", 0);
        mAccessList.grantFullAccess("pkg", 1);
        mAccessList.grantFullAccess("pkg2", 3);

        ByteArrayOutputStream output = new ByteArrayOutputStream();
        XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
        out.setOutput(output, Encoding.UTF_8.name());
        mAccessList.writeXml(out);
        out.flush();

        assertEquals(TEST_XML, output.toString(Encoding.UTF_8.name()));
    }

    @Test
    public void testDeSerialization() throws XmlPullParserException, IOException {
        ByteArrayInputStream input = new ByteArrayInputStream(TEST_XML.getBytes());
        XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
        parser.setInput(input, Encoding.UTF_8.name());

        mAccessList.readXml(parser);

        assertTrue(mAccessList.hasFullAccess("pkg", 0));
        assertTrue(mAccessList.hasFullAccess("pkg1", 0));
        assertTrue(mAccessList.hasFullAccess("pkg", 1));
        assertTrue(mAccessList.hasFullAccess("pkg2", 3));

        assertFalse(mAccessList.hasFullAccess("pkg3", 0));
        assertFalse(mAccessList.hasFullAccess("pkg1", 1));
        assertFalse(mAccessList.hasFullAccess("pkg", 3));
        assertFalse(mAccessList.hasFullAccess("pkg", 2));
    }
}
 No newline at end of file