Loading services/core/java/com/android/server/slice/SliceFullAccessList.java 0 → 100644 +131 −0 Original line number Original line 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) {} } services/core/java/com/android/server/slice/SliceManagerService.java +76 −15 Original line number Original line Diff line number Diff line Loading @@ -39,6 +39,7 @@ import android.content.pm.ResolveInfo; import android.database.ContentObserver; import android.database.ContentObserver; import android.net.Uri; import android.net.Uri; import android.os.Binder; import android.os.Binder; import android.os.Environment; import android.os.Handler; import android.os.Handler; import android.os.IBinder; import android.os.IBinder; import android.os.Looper; import android.os.Looper; Loading @@ -47,7 +48,10 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArrayMap; import android.util.ArraySet; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Log; import android.util.Log; import android.util.Slog; import android.util.Xml.Encoding; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; Loading @@ -57,6 +61,16 @@ import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.ServiceThread; import com.android.server.SystemService; 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.ArrayList; import java.util.List; import java.util.List; import java.util.Objects; import java.util.Objects; Loading @@ -77,6 +91,8 @@ public class SliceManagerService extends ISliceManager.Stub { private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>(); private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>(); private final Handler mHandler; private final Handler mHandler; private final ContentObserver mObserver; private final ContentObserver mObserver; private final AtomicFile mSliceAccessFile; private final SliceFullAccessList mAccessList; public SliceManagerService(Context context) { public SliceManagerService(Context context) { this(context, createHandler().getLooper()); this(context, createHandler().getLooper()); Loading @@ -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 ----- /// ----- Lifecycle stuff ----- Loading Loading @@ -175,11 +206,11 @@ public class SliceManagerService extends ISliceManager.Stub { == PERMISSION_GRANTED) { == PERMISSION_GRANTED) { return SliceManager.PERMISSION_GRANTED; return SliceManager.PERMISSION_GRANTED; } } if (hasFullSliceAccess(pkg, uid)) { if (hasFullSliceAccess(pkg, UserHandle.getUserId(uid))) { return SliceManager.PERMISSION_GRANTED; return SliceManager.PERMISSION_GRANTED; } } synchronized (mLock) { synchronized (mLock) { if (mUserGrants.contains(new SliceGrant(uri, pkg))) { if (mUserGrants.contains(new SliceGrant(uri, pkg, UserHandle.getUserId(uid)))) { return SliceManager.PERMISSION_USER_GRANTED; return SliceManager.PERMISSION_USER_GRANTED; } } } } Loading @@ -192,10 +223,13 @@ public class SliceManagerService extends ISliceManager.Stub { getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS, getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS, "Slice granting requires MANAGE_SLICE_PERMISSIONS"); "Slice granting requires MANAGE_SLICE_PERMISSIONS"); if (allSlices) { if (allSlices) { // TODO: Manage full access grants. mAccessList.grantFullAccess(pkg, Binder.getCallingUserHandle().getIdentifier()); mHandler.post(mSaveAccessList); } else { } else { synchronized (mLock) { synchronized (mLock) { mUserGrants.add(new SliceGrant(uri, pkg)); mUserGrants.add(new SliceGrant(uri, pkg, Binder.getCallingUserHandle().getIdentifier())); } } } long ident = Binder.clearCallingIdentity(); long ident = Binder.clearCallingIdentity(); try { try { Loading @@ -203,7 +237,6 @@ public class SliceManagerService extends ISliceManager.Stub { } finally { } finally { Binder.restoreCallingIdentity(ident); Binder.restoreCallingIdentity(ident); } } } synchronized (mLock) { synchronized (mLock) { for (PinnedSliceState p : mPinnedSlicesByUri.values()) { for (PinnedSliceState p : mPinnedSlicesByUri.values()) { p.recheckPackage(pkg); p.recheckPackage(pkg); Loading Loading @@ -260,7 +293,7 @@ public class SliceManagerService extends ISliceManager.Stub { protected int checkAccess(String pkg, Uri uri, int uid, int pid) { protected int checkAccess(String pkg, Uri uri, int uid, int pid) { int user = UserHandle.getUserId(uid); int user = UserHandle.getUserId(uid); // Check for default launcher/assistant. // Check for default launcher/assistant. if (!hasFullSliceAccess(pkg, uid)) { if (!hasFullSliceAccess(pkg, user)) { // Also allow things with uri access. // Also allow things with uri access. if (getContext().checkUriPermission(uri, pid, uid, if (getContext().checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != PERMISSION_GRANTED) { Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != PERMISSION_GRANTED) { Loading Loading @@ -411,8 +444,7 @@ public class SliceManagerService extends ISliceManager.Stub { } } private boolean isGrantedFullAccess(String pkg, int userId) { private boolean isGrantedFullAccess(String pkg, int userId) { // TODO: This will be user granted access, if we allow this through a prompt. return mAccessList.hasFullAccess(pkg, userId); return false; } } private static ServiceThread createHandler() { private static ServiceThread createHandler() { Loading @@ -422,6 +454,32 @@ public class SliceManagerService extends ISliceManager.Stub { return handlerThread; 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 { public static class Lifecycle extends SystemService { private SliceManagerService mService; private SliceManagerService mService; Loading Loading @@ -456,10 +514,12 @@ public class SliceManagerService extends ISliceManager.Stub { private class SliceGrant { private class SliceGrant { private final Uri mUri; private final Uri mUri; private final String mPkg; private final String mPkg; private final int mUserId; public SliceGrant(Uri uri, String pkg) { public SliceGrant(Uri uri, String pkg, int userId) { mUri = uri; mUri = uri; mPkg = pkg; mPkg = pkg; mUserId = userId; } } @Override @Override Loading @@ -471,7 +531,8 @@ public class SliceManagerService extends ISliceManager.Stub { public boolean equals(Object obj) { public boolean equals(Object obj) { if (!(obj instanceof SliceGrant)) return false; if (!(obj instanceof SliceGrant)) return false; SliceGrant other = (SliceGrant) obj; 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); } } } } } } services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java 0 → 100644 +103 −0 Original line number Original line 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 Loading
services/core/java/com/android/server/slice/SliceFullAccessList.java 0 → 100644 +131 −0 Original line number Original line 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) {} }
services/core/java/com/android/server/slice/SliceManagerService.java +76 −15 Original line number Original line Diff line number Diff line Loading @@ -39,6 +39,7 @@ import android.content.pm.ResolveInfo; import android.database.ContentObserver; import android.database.ContentObserver; import android.net.Uri; import android.net.Uri; import android.os.Binder; import android.os.Binder; import android.os.Environment; import android.os.Handler; import android.os.Handler; import android.os.IBinder; import android.os.IBinder; import android.os.Looper; import android.os.Looper; Loading @@ -47,7 +48,10 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArrayMap; import android.util.ArraySet; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Log; import android.util.Log; import android.util.Slog; import android.util.Xml.Encoding; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; Loading @@ -57,6 +61,16 @@ import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.ServiceThread; import com.android.server.SystemService; 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.ArrayList; import java.util.List; import java.util.List; import java.util.Objects; import java.util.Objects; Loading @@ -77,6 +91,8 @@ public class SliceManagerService extends ISliceManager.Stub { private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>(); private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>(); private final Handler mHandler; private final Handler mHandler; private final ContentObserver mObserver; private final ContentObserver mObserver; private final AtomicFile mSliceAccessFile; private final SliceFullAccessList mAccessList; public SliceManagerService(Context context) { public SliceManagerService(Context context) { this(context, createHandler().getLooper()); this(context, createHandler().getLooper()); Loading @@ -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 ----- /// ----- Lifecycle stuff ----- Loading Loading @@ -175,11 +206,11 @@ public class SliceManagerService extends ISliceManager.Stub { == PERMISSION_GRANTED) { == PERMISSION_GRANTED) { return SliceManager.PERMISSION_GRANTED; return SliceManager.PERMISSION_GRANTED; } } if (hasFullSliceAccess(pkg, uid)) { if (hasFullSliceAccess(pkg, UserHandle.getUserId(uid))) { return SliceManager.PERMISSION_GRANTED; return SliceManager.PERMISSION_GRANTED; } } synchronized (mLock) { synchronized (mLock) { if (mUserGrants.contains(new SliceGrant(uri, pkg))) { if (mUserGrants.contains(new SliceGrant(uri, pkg, UserHandle.getUserId(uid)))) { return SliceManager.PERMISSION_USER_GRANTED; return SliceManager.PERMISSION_USER_GRANTED; } } } } Loading @@ -192,10 +223,13 @@ public class SliceManagerService extends ISliceManager.Stub { getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS, getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS, "Slice granting requires MANAGE_SLICE_PERMISSIONS"); "Slice granting requires MANAGE_SLICE_PERMISSIONS"); if (allSlices) { if (allSlices) { // TODO: Manage full access grants. mAccessList.grantFullAccess(pkg, Binder.getCallingUserHandle().getIdentifier()); mHandler.post(mSaveAccessList); } else { } else { synchronized (mLock) { synchronized (mLock) { mUserGrants.add(new SliceGrant(uri, pkg)); mUserGrants.add(new SliceGrant(uri, pkg, Binder.getCallingUserHandle().getIdentifier())); } } } long ident = Binder.clearCallingIdentity(); long ident = Binder.clearCallingIdentity(); try { try { Loading @@ -203,7 +237,6 @@ public class SliceManagerService extends ISliceManager.Stub { } finally { } finally { Binder.restoreCallingIdentity(ident); Binder.restoreCallingIdentity(ident); } } } synchronized (mLock) { synchronized (mLock) { for (PinnedSliceState p : mPinnedSlicesByUri.values()) { for (PinnedSliceState p : mPinnedSlicesByUri.values()) { p.recheckPackage(pkg); p.recheckPackage(pkg); Loading Loading @@ -260,7 +293,7 @@ public class SliceManagerService extends ISliceManager.Stub { protected int checkAccess(String pkg, Uri uri, int uid, int pid) { protected int checkAccess(String pkg, Uri uri, int uid, int pid) { int user = UserHandle.getUserId(uid); int user = UserHandle.getUserId(uid); // Check for default launcher/assistant. // Check for default launcher/assistant. if (!hasFullSliceAccess(pkg, uid)) { if (!hasFullSliceAccess(pkg, user)) { // Also allow things with uri access. // Also allow things with uri access. if (getContext().checkUriPermission(uri, pid, uid, if (getContext().checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != PERMISSION_GRANTED) { Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != PERMISSION_GRANTED) { Loading Loading @@ -411,8 +444,7 @@ public class SliceManagerService extends ISliceManager.Stub { } } private boolean isGrantedFullAccess(String pkg, int userId) { private boolean isGrantedFullAccess(String pkg, int userId) { // TODO: This will be user granted access, if we allow this through a prompt. return mAccessList.hasFullAccess(pkg, userId); return false; } } private static ServiceThread createHandler() { private static ServiceThread createHandler() { Loading @@ -422,6 +454,32 @@ public class SliceManagerService extends ISliceManager.Stub { return handlerThread; 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 { public static class Lifecycle extends SystemService { private SliceManagerService mService; private SliceManagerService mService; Loading Loading @@ -456,10 +514,12 @@ public class SliceManagerService extends ISliceManager.Stub { private class SliceGrant { private class SliceGrant { private final Uri mUri; private final Uri mUri; private final String mPkg; private final String mPkg; private final int mUserId; public SliceGrant(Uri uri, String pkg) { public SliceGrant(Uri uri, String pkg, int userId) { mUri = uri; mUri = uri; mPkg = pkg; mPkg = pkg; mUserId = userId; } } @Override @Override Loading @@ -471,7 +531,8 @@ public class SliceManagerService extends ISliceManager.Stub { public boolean equals(Object obj) { public boolean equals(Object obj) { if (!(obj instanceof SliceGrant)) return false; if (!(obj instanceof SliceGrant)) return false; SliceGrant other = (SliceGrant) obj; 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); } } } } } }
services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java 0 → 100644 +103 −0 Original line number Original line 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