Loading core/java/android/app/slice/ISliceManager.aidl +7 −3 Original line number Diff line number Diff line Loading @@ -25,11 +25,15 @@ interface ISliceManager { void unpinSlice(String pkg, in Uri uri, in IBinder token); boolean hasSliceAccess(String pkg); SliceSpec[] getPinnedSpecs(in Uri uri, String pkg); int checkSlicePermission(in Uri uri, String pkg, int pid, int uid, in String[] autoGrantPermissions); void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices); Uri[] getPinnedSlices(String pkg); byte[] getBackupPayload(int user); void applyRestore(in byte[] payload, int user); // Perms. void grantSlicePermission(String callingPkg, String toPkg, in Uri uri); void revokeSlicePermission(String callingPkg, String toPkg, in Uri uri); int checkSlicePermission(in Uri uri, String pkg, int pid, int uid, in String[] autoGrantPermissions); void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices); } core/java/android/app/slice/SliceManager.java +17 −39 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.app.slice; import static android.content.pm.PackageManager.PERMISSION_DENIED; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; Loading Loading @@ -100,22 +102,6 @@ public class SliceManager { private final Context mContext; private final IBinder mToken = new Binder(); /** * Permission denied. * @hide */ public static final int PERMISSION_DENIED = -1; /** * Permission granted. * @hide */ public static final int PERMISSION_GRANTED = 0; /** * Permission just granted by the user, and should be granted uri permission as well. * @hide */ public static final int PERMISSION_USER_GRANTED = 1; /** * @hide */ Loading Loading @@ -417,9 +403,11 @@ public class SliceManager { * @see #grantSlicePermission(String, Uri) */ public @PermissionResult int checkSlicePermission(@NonNull Uri uri, int pid, int uid) { // TODO: Switch off Uri permissions. return mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); try { return mService.checkSlicePermission(uri, null, pid, uid, null); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** Loading @@ -431,11 +419,11 @@ public class SliceManager { * @see #revokeSlicePermission */ public void grantSlicePermission(@NonNull String toPackage, @NonNull Uri uri) { // TODO: Switch off Uri permissions. mContext.grantUriPermission(toPackage, uri, Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); try { mService.grantSlicePermission(mContext.getPackageName(), toPackage, uri); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** Loading @@ -453,11 +441,11 @@ public class SliceManager { * @see #grantSlicePermission */ public void revokeSlicePermission(@NonNull String toPackage, @NonNull Uri uri) { // TODO: Switch off Uri permissions. mContext.revokeUriPermission(toPackage, uri, Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); try { mService.revokeSlicePermission(mContext.getPackageName(), toPackage, uri); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** Loading @@ -478,16 +466,6 @@ public class SliceManager { throw new SecurityException("User " + uid + " does not have slice permission for " + uri + "."); } if (result == PERMISSION_USER_GRANTED) { // We just had a user grant of this permission and need to grant this to the app // permanently. mContext.grantUriPermission(pkg, uri.buildUpon().path("").build(), Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); // Notify a change has happened because we just granted a permission. mContext.getContentResolver().notifyChange(uri, null); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading services/core/java/com/android/server/slice/DirtyTracker.java 0 → 100644 +35 −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 org.xmlpull.v1.XmlSerializer; import java.io.IOException; /** * A parent object that cares when a Persistable changes and will schedule a serialization * in response to the onPersistableDirty callback. */ public interface DirtyTracker { void onPersistableDirty(Persistable obj); /** * An object that can be written to XML. */ interface Persistable { String getFileName(); void writeTo(XmlSerializer out) throws IOException; } } services/core/java/com/android/server/slice/SliceClientPermissions.java 0 → 100644 +354 −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.annotation.NonNull; import android.content.ContentResolver; import android.net.Uri; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import com.android.server.slice.DirtyTracker.Persistable; import com.android.server.slice.SlicePermissionManager.PkgUser; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; public class SliceClientPermissions implements DirtyTracker, Persistable { private static final String TAG = "SliceClientPermissions"; static final String TAG_CLIENT = "client"; private static final String TAG_AUTHORITY = "authority"; private static final String TAG_PATH = "path"; private static final String NAMESPACE = null; private static final String ATTR_PKG = "pkg"; private static final String ATTR_AUTHORITY = "authority"; private static final String ATTR_FULL_ACCESS = "fullAccess"; private final PkgUser mPkg; // Keyed off (authority, userId) rather than the standard (pkg, userId) private final ArrayMap<PkgUser, SliceAuthority> mAuths = new ArrayMap<>(); private final DirtyTracker mTracker; private boolean mHasFullAccess; public SliceClientPermissions(@NonNull PkgUser pkg, @NonNull DirtyTracker tracker) { mPkg = pkg; mTracker = tracker; } public PkgUser getPkg() { return mPkg; } public synchronized Collection<SliceAuthority> getAuthorities() { return new ArrayList<>(mAuths.values()); } public synchronized SliceAuthority getOrCreateAuthority(PkgUser authority, PkgUser provider) { SliceAuthority ret = mAuths.get(authority); if (ret == null) { ret = new SliceAuthority(authority.getPkg(), provider, this); mAuths.put(authority, ret); onPersistableDirty(ret); } return ret; } public synchronized SliceAuthority getAuthority(PkgUser authority) { return mAuths.get(authority); } public boolean hasFullAccess() { return mHasFullAccess; } public void setHasFullAccess(boolean hasFullAccess) { if (mHasFullAccess == hasFullAccess) return; mHasFullAccess = hasFullAccess; mTracker.onPersistableDirty(this); } public void removeAuthority(String authority, int userId) { if (mAuths.remove(new PkgUser(authority, userId)) != null) { mTracker.onPersistableDirty(this); } } public synchronized boolean hasPermission(Uri uri, int userId) { if (!Objects.equals(ContentResolver.SCHEME_CONTENT, uri.getScheme())) return false; SliceAuthority authority = getAuthority(new PkgUser(uri.getAuthority(), userId)); return authority != null && authority.hasPermission(uri.getPathSegments()); } public void grantUri(Uri uri, PkgUser providerPkg) { SliceAuthority authority = getOrCreateAuthority( new PkgUser(uri.getAuthority(), providerPkg.getUserId()), providerPkg); authority.addPath(uri.getPathSegments()); } public void revokeUri(Uri uri, PkgUser providerPkg) { SliceAuthority authority = getOrCreateAuthority( new PkgUser(uri.getAuthority(), providerPkg.getUserId()), providerPkg); authority.removePath(uri.getPathSegments()); } public void clear() { if (!mHasFullAccess && mAuths.isEmpty()) return; mHasFullAccess = false; mAuths.clear(); onPersistableDirty(this); } @Override public void onPersistableDirty(Persistable obj) { mTracker.onPersistableDirty(this); } @Override public String getFileName() { return getFileName(mPkg); } public synchronized void writeTo(XmlSerializer out) throws IOException { out.startTag(NAMESPACE, TAG_CLIENT); out.attribute(NAMESPACE, ATTR_PKG, mPkg.toString()); out.attribute(NAMESPACE, ATTR_FULL_ACCESS, mHasFullAccess ? "1" : "0"); final int N = mAuths.size(); for (int i = 0; i < N; i++) { out.startTag(NAMESPACE, TAG_AUTHORITY); out.attribute(NAMESPACE, ATTR_AUTHORITY, mAuths.valueAt(i).mAuthority); out.attribute(NAMESPACE, ATTR_PKG, mAuths.valueAt(i).mPkg.toString()); mAuths.valueAt(i).writeTo(out); out.endTag(NAMESPACE, TAG_AUTHORITY); } out.endTag(NAMESPACE, TAG_CLIENT); } public static SliceClientPermissions createFrom(XmlPullParser parser, DirtyTracker tracker) throws XmlPullParserException, IOException { // Get to the beginning of the provider. while (parser.getEventType() != XmlPullParser.START_TAG || !TAG_CLIENT.equals(parser.getName())) { parser.next(); } int depth = parser.getDepth(); PkgUser pkgUser = new PkgUser(parser.getAttributeValue(NAMESPACE, ATTR_PKG)); SliceClientPermissions provider = new SliceClientPermissions(pkgUser, tracker); String fullAccess = parser.getAttributeValue(NAMESPACE, ATTR_FULL_ACCESS); if (fullAccess == null) { fullAccess = "0"; } provider.mHasFullAccess = Integer.parseInt(fullAccess) != 0; parser.next(); while (parser.getDepth() > depth) { if (parser.getEventType() == XmlPullParser.START_TAG && TAG_AUTHORITY.equals(parser.getName())) { try { PkgUser pkg = new PkgUser(parser.getAttributeValue(NAMESPACE, ATTR_PKG)); SliceAuthority authority = new SliceAuthority( parser.getAttributeValue(NAMESPACE, ATTR_AUTHORITY), pkg, provider); authority.readFrom(parser); provider.mAuths.put(new PkgUser(authority.getAuthority(), pkg.getUserId()), authority); } catch (IllegalArgumentException e) { Slog.e(TAG, "Couldn't read PkgUser", e); } } parser.next(); } return provider; } public static String getFileName(PkgUser pkg) { return String.format("client_%s", pkg.toString()); } public static class SliceAuthority implements Persistable { public static final String DELIMITER = "/"; private final String mAuthority; private final DirtyTracker mTracker; private final PkgUser mPkg; private final ArraySet<String[]> mPaths = new ArraySet<>(); public SliceAuthority(String authority, PkgUser pkg, DirtyTracker tracker) { mAuthority = authority; mPkg = pkg; mTracker = tracker; } public String getAuthority() { return mAuthority; } public PkgUser getPkg() { return mPkg; } void addPath(List<String> path) { String[] pathSegs = path.toArray(new String[path.size()]); for (int i = mPaths.size() - 1; i >= 0; i--) { String[] existing = mPaths.valueAt(i); if (isPathPrefixMatch(existing, pathSegs)) { // Nothing to add here. return; } if (isPathPrefixMatch(pathSegs, existing)) { mPaths.removeAt(i); } } mPaths.add(pathSegs); mTracker.onPersistableDirty(this); } void removePath(List<String> path) { boolean changed = false; String[] pathSegs = path.toArray(new String[path.size()]); for (int i = mPaths.size() - 1; i >= 0; i--) { String[] existing = mPaths.valueAt(i); if (isPathPrefixMatch(pathSegs, existing)) { changed = true; mPaths.removeAt(i); } } if (changed) { mTracker.onPersistableDirty(this); } } public synchronized Collection<String[]> getPaths() { return new ArraySet<>(mPaths); } public boolean hasPermission(List<String> path) { for (String[] p : mPaths) { if (isPathPrefixMatch(p, path.toArray(new String[path.size()]))) { return true; } } return false; } private boolean isPathPrefixMatch(String[] prefix, String[] path) { final int prefixSize = prefix.length; if (path.length < prefixSize) return false; for (int i = 0; i < prefixSize; i++) { if (!Objects.equals(path[i], prefix[i])) { return false; } } return true; } @Override public String getFileName() { return null; } public synchronized void writeTo(XmlSerializer out) throws IOException { final int N = mPaths.size(); for (int i = 0; i < N; i++) { out.startTag(NAMESPACE, TAG_PATH); out.text(encodeSegments(mPaths.valueAt(i))); out.endTag(NAMESPACE, TAG_PATH); } } public synchronized void readFrom(XmlPullParser parser) throws IOException, XmlPullParserException { parser.next(); int depth = parser.getDepth(); while (parser.getDepth() >= depth) { if (parser.getEventType() == XmlPullParser.START_TAG && TAG_PATH.equals(parser.getName())) { mPaths.add(decodeSegments(parser.nextText())); } parser.next(); } } private String encodeSegments(String[] s) { String[] out = new String[s.length]; for (int i = 0; i < s.length; i++) { out[i] = Uri.encode(s[i]); } return TextUtils.join(DELIMITER, out); } private String[] decodeSegments(String s) { String[] sets = s.split(DELIMITER, -1); for (int i = 0; i < sets.length; i++) { sets[i] = Uri.decode(sets[i]); } return sets; } /** * Only for testing, no deep equality of these are done normally. */ @Override public boolean equals(Object obj) { if (!getClass().equals(obj != null ? obj.getClass() : null)) return false; SliceAuthority other = (SliceAuthority) obj; if (mPaths.size() != other.mPaths.size()) return false; ArrayList<String[]> p1 = new ArrayList<>(mPaths); ArrayList<String[]> p2 = new ArrayList<>(other.mPaths); p1.sort(Comparator.comparing(o -> TextUtils.join(",", o))); p2.sort(Comparator.comparing(o -> TextUtils.join(",", o))); for (int i = 0; i < p1.size(); i++) { String[] a1 = p1.get(i); String[] a2 = p2.get(i); if (a1.length != a2.length) return false; for (int j = 0; j < a1.length; j++) { if (!Objects.equals(a1[j], a2[j])) return false; } } return Objects.equals(mAuthority, other.mAuthority) && Objects.equals(mPkg, other.mPkg); } @Override public String toString() { return String.format("(%s, %s: %s)", mAuthority, mPkg.toString(), pathToString(mPaths)); } private String pathToString(ArraySet<String[]> paths) { return TextUtils.join(", ", paths.stream().map(s -> TextUtils.join("/", s)) .collect(Collectors.toList())); } } } services/core/java/com/android/server/slice/SliceManagerService.java +92 −133 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/app/slice/ISliceManager.aidl +7 −3 Original line number Diff line number Diff line Loading @@ -25,11 +25,15 @@ interface ISliceManager { void unpinSlice(String pkg, in Uri uri, in IBinder token); boolean hasSliceAccess(String pkg); SliceSpec[] getPinnedSpecs(in Uri uri, String pkg); int checkSlicePermission(in Uri uri, String pkg, int pid, int uid, in String[] autoGrantPermissions); void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices); Uri[] getPinnedSlices(String pkg); byte[] getBackupPayload(int user); void applyRestore(in byte[] payload, int user); // Perms. void grantSlicePermission(String callingPkg, String toPkg, in Uri uri); void revokeSlicePermission(String callingPkg, String toPkg, in Uri uri); int checkSlicePermission(in Uri uri, String pkg, int pid, int uid, in String[] autoGrantPermissions); void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices); }
core/java/android/app/slice/SliceManager.java +17 −39 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.app.slice; import static android.content.pm.PackageManager.PERMISSION_DENIED; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; Loading Loading @@ -100,22 +102,6 @@ public class SliceManager { private final Context mContext; private final IBinder mToken = new Binder(); /** * Permission denied. * @hide */ public static final int PERMISSION_DENIED = -1; /** * Permission granted. * @hide */ public static final int PERMISSION_GRANTED = 0; /** * Permission just granted by the user, and should be granted uri permission as well. * @hide */ public static final int PERMISSION_USER_GRANTED = 1; /** * @hide */ Loading Loading @@ -417,9 +403,11 @@ public class SliceManager { * @see #grantSlicePermission(String, Uri) */ public @PermissionResult int checkSlicePermission(@NonNull Uri uri, int pid, int uid) { // TODO: Switch off Uri permissions. return mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); try { return mService.checkSlicePermission(uri, null, pid, uid, null); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** Loading @@ -431,11 +419,11 @@ public class SliceManager { * @see #revokeSlicePermission */ public void grantSlicePermission(@NonNull String toPackage, @NonNull Uri uri) { // TODO: Switch off Uri permissions. mContext.grantUriPermission(toPackage, uri, Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); try { mService.grantSlicePermission(mContext.getPackageName(), toPackage, uri); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** Loading @@ -453,11 +441,11 @@ public class SliceManager { * @see #grantSlicePermission */ public void revokeSlicePermission(@NonNull String toPackage, @NonNull Uri uri) { // TODO: Switch off Uri permissions. mContext.revokeUriPermission(toPackage, uri, Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); try { mService.revokeSlicePermission(mContext.getPackageName(), toPackage, uri); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** Loading @@ -478,16 +466,6 @@ public class SliceManager { throw new SecurityException("User " + uid + " does not have slice permission for " + uri + "."); } if (result == PERMISSION_USER_GRANTED) { // We just had a user grant of this permission and need to grant this to the app // permanently. mContext.grantUriPermission(pkg, uri.buildUpon().path("").build(), Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); // Notify a change has happened because we just granted a permission. mContext.getContentResolver().notifyChange(uri, null); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading
services/core/java/com/android/server/slice/DirtyTracker.java 0 → 100644 +35 −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 org.xmlpull.v1.XmlSerializer; import java.io.IOException; /** * A parent object that cares when a Persistable changes and will schedule a serialization * in response to the onPersistableDirty callback. */ public interface DirtyTracker { void onPersistableDirty(Persistable obj); /** * An object that can be written to XML. */ interface Persistable { String getFileName(); void writeTo(XmlSerializer out) throws IOException; } }
services/core/java/com/android/server/slice/SliceClientPermissions.java 0 → 100644 +354 −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.annotation.NonNull; import android.content.ContentResolver; import android.net.Uri; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import com.android.server.slice.DirtyTracker.Persistable; import com.android.server.slice.SlicePermissionManager.PkgUser; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; public class SliceClientPermissions implements DirtyTracker, Persistable { private static final String TAG = "SliceClientPermissions"; static final String TAG_CLIENT = "client"; private static final String TAG_AUTHORITY = "authority"; private static final String TAG_PATH = "path"; private static final String NAMESPACE = null; private static final String ATTR_PKG = "pkg"; private static final String ATTR_AUTHORITY = "authority"; private static final String ATTR_FULL_ACCESS = "fullAccess"; private final PkgUser mPkg; // Keyed off (authority, userId) rather than the standard (pkg, userId) private final ArrayMap<PkgUser, SliceAuthority> mAuths = new ArrayMap<>(); private final DirtyTracker mTracker; private boolean mHasFullAccess; public SliceClientPermissions(@NonNull PkgUser pkg, @NonNull DirtyTracker tracker) { mPkg = pkg; mTracker = tracker; } public PkgUser getPkg() { return mPkg; } public synchronized Collection<SliceAuthority> getAuthorities() { return new ArrayList<>(mAuths.values()); } public synchronized SliceAuthority getOrCreateAuthority(PkgUser authority, PkgUser provider) { SliceAuthority ret = mAuths.get(authority); if (ret == null) { ret = new SliceAuthority(authority.getPkg(), provider, this); mAuths.put(authority, ret); onPersistableDirty(ret); } return ret; } public synchronized SliceAuthority getAuthority(PkgUser authority) { return mAuths.get(authority); } public boolean hasFullAccess() { return mHasFullAccess; } public void setHasFullAccess(boolean hasFullAccess) { if (mHasFullAccess == hasFullAccess) return; mHasFullAccess = hasFullAccess; mTracker.onPersistableDirty(this); } public void removeAuthority(String authority, int userId) { if (mAuths.remove(new PkgUser(authority, userId)) != null) { mTracker.onPersistableDirty(this); } } public synchronized boolean hasPermission(Uri uri, int userId) { if (!Objects.equals(ContentResolver.SCHEME_CONTENT, uri.getScheme())) return false; SliceAuthority authority = getAuthority(new PkgUser(uri.getAuthority(), userId)); return authority != null && authority.hasPermission(uri.getPathSegments()); } public void grantUri(Uri uri, PkgUser providerPkg) { SliceAuthority authority = getOrCreateAuthority( new PkgUser(uri.getAuthority(), providerPkg.getUserId()), providerPkg); authority.addPath(uri.getPathSegments()); } public void revokeUri(Uri uri, PkgUser providerPkg) { SliceAuthority authority = getOrCreateAuthority( new PkgUser(uri.getAuthority(), providerPkg.getUserId()), providerPkg); authority.removePath(uri.getPathSegments()); } public void clear() { if (!mHasFullAccess && mAuths.isEmpty()) return; mHasFullAccess = false; mAuths.clear(); onPersistableDirty(this); } @Override public void onPersistableDirty(Persistable obj) { mTracker.onPersistableDirty(this); } @Override public String getFileName() { return getFileName(mPkg); } public synchronized void writeTo(XmlSerializer out) throws IOException { out.startTag(NAMESPACE, TAG_CLIENT); out.attribute(NAMESPACE, ATTR_PKG, mPkg.toString()); out.attribute(NAMESPACE, ATTR_FULL_ACCESS, mHasFullAccess ? "1" : "0"); final int N = mAuths.size(); for (int i = 0; i < N; i++) { out.startTag(NAMESPACE, TAG_AUTHORITY); out.attribute(NAMESPACE, ATTR_AUTHORITY, mAuths.valueAt(i).mAuthority); out.attribute(NAMESPACE, ATTR_PKG, mAuths.valueAt(i).mPkg.toString()); mAuths.valueAt(i).writeTo(out); out.endTag(NAMESPACE, TAG_AUTHORITY); } out.endTag(NAMESPACE, TAG_CLIENT); } public static SliceClientPermissions createFrom(XmlPullParser parser, DirtyTracker tracker) throws XmlPullParserException, IOException { // Get to the beginning of the provider. while (parser.getEventType() != XmlPullParser.START_TAG || !TAG_CLIENT.equals(parser.getName())) { parser.next(); } int depth = parser.getDepth(); PkgUser pkgUser = new PkgUser(parser.getAttributeValue(NAMESPACE, ATTR_PKG)); SliceClientPermissions provider = new SliceClientPermissions(pkgUser, tracker); String fullAccess = parser.getAttributeValue(NAMESPACE, ATTR_FULL_ACCESS); if (fullAccess == null) { fullAccess = "0"; } provider.mHasFullAccess = Integer.parseInt(fullAccess) != 0; parser.next(); while (parser.getDepth() > depth) { if (parser.getEventType() == XmlPullParser.START_TAG && TAG_AUTHORITY.equals(parser.getName())) { try { PkgUser pkg = new PkgUser(parser.getAttributeValue(NAMESPACE, ATTR_PKG)); SliceAuthority authority = new SliceAuthority( parser.getAttributeValue(NAMESPACE, ATTR_AUTHORITY), pkg, provider); authority.readFrom(parser); provider.mAuths.put(new PkgUser(authority.getAuthority(), pkg.getUserId()), authority); } catch (IllegalArgumentException e) { Slog.e(TAG, "Couldn't read PkgUser", e); } } parser.next(); } return provider; } public static String getFileName(PkgUser pkg) { return String.format("client_%s", pkg.toString()); } public static class SliceAuthority implements Persistable { public static final String DELIMITER = "/"; private final String mAuthority; private final DirtyTracker mTracker; private final PkgUser mPkg; private final ArraySet<String[]> mPaths = new ArraySet<>(); public SliceAuthority(String authority, PkgUser pkg, DirtyTracker tracker) { mAuthority = authority; mPkg = pkg; mTracker = tracker; } public String getAuthority() { return mAuthority; } public PkgUser getPkg() { return mPkg; } void addPath(List<String> path) { String[] pathSegs = path.toArray(new String[path.size()]); for (int i = mPaths.size() - 1; i >= 0; i--) { String[] existing = mPaths.valueAt(i); if (isPathPrefixMatch(existing, pathSegs)) { // Nothing to add here. return; } if (isPathPrefixMatch(pathSegs, existing)) { mPaths.removeAt(i); } } mPaths.add(pathSegs); mTracker.onPersistableDirty(this); } void removePath(List<String> path) { boolean changed = false; String[] pathSegs = path.toArray(new String[path.size()]); for (int i = mPaths.size() - 1; i >= 0; i--) { String[] existing = mPaths.valueAt(i); if (isPathPrefixMatch(pathSegs, existing)) { changed = true; mPaths.removeAt(i); } } if (changed) { mTracker.onPersistableDirty(this); } } public synchronized Collection<String[]> getPaths() { return new ArraySet<>(mPaths); } public boolean hasPermission(List<String> path) { for (String[] p : mPaths) { if (isPathPrefixMatch(p, path.toArray(new String[path.size()]))) { return true; } } return false; } private boolean isPathPrefixMatch(String[] prefix, String[] path) { final int prefixSize = prefix.length; if (path.length < prefixSize) return false; for (int i = 0; i < prefixSize; i++) { if (!Objects.equals(path[i], prefix[i])) { return false; } } return true; } @Override public String getFileName() { return null; } public synchronized void writeTo(XmlSerializer out) throws IOException { final int N = mPaths.size(); for (int i = 0; i < N; i++) { out.startTag(NAMESPACE, TAG_PATH); out.text(encodeSegments(mPaths.valueAt(i))); out.endTag(NAMESPACE, TAG_PATH); } } public synchronized void readFrom(XmlPullParser parser) throws IOException, XmlPullParserException { parser.next(); int depth = parser.getDepth(); while (parser.getDepth() >= depth) { if (parser.getEventType() == XmlPullParser.START_TAG && TAG_PATH.equals(parser.getName())) { mPaths.add(decodeSegments(parser.nextText())); } parser.next(); } } private String encodeSegments(String[] s) { String[] out = new String[s.length]; for (int i = 0; i < s.length; i++) { out[i] = Uri.encode(s[i]); } return TextUtils.join(DELIMITER, out); } private String[] decodeSegments(String s) { String[] sets = s.split(DELIMITER, -1); for (int i = 0; i < sets.length; i++) { sets[i] = Uri.decode(sets[i]); } return sets; } /** * Only for testing, no deep equality of these are done normally. */ @Override public boolean equals(Object obj) { if (!getClass().equals(obj != null ? obj.getClass() : null)) return false; SliceAuthority other = (SliceAuthority) obj; if (mPaths.size() != other.mPaths.size()) return false; ArrayList<String[]> p1 = new ArrayList<>(mPaths); ArrayList<String[]> p2 = new ArrayList<>(other.mPaths); p1.sort(Comparator.comparing(o -> TextUtils.join(",", o))); p2.sort(Comparator.comparing(o -> TextUtils.join(",", o))); for (int i = 0; i < p1.size(); i++) { String[] a1 = p1.get(i); String[] a2 = p2.get(i); if (a1.length != a2.length) return false; for (int j = 0; j < a1.length; j++) { if (!Objects.equals(a1[j], a2[j])) return false; } } return Objects.equals(mAuthority, other.mAuthority) && Objects.equals(mPkg, other.mPkg); } @Override public String toString() { return String.format("(%s, %s: %s)", mAuthority, mPkg.toString(), pathToString(mPaths)); } private String pathToString(ArraySet<String[]> paths) { return TextUtils.join(", ", paths.stream().map(s -> TextUtils.join("/", s)) .collect(Collectors.toList())); } } }
services/core/java/com/android/server/slice/SliceManagerService.java +92 −133 File changed.Preview size limit exceeded, changes collapsed. Show changes