diff options
5 files changed, 91 insertions, 96 deletions
diff --git a/core/java/android/view/KeyboardShortcutInfo.java b/core/java/android/view/KeyboardShortcutInfo.java index 2660e74dcb20..2075d77a9871 100644 --- a/core/java/android/view/KeyboardShortcutInfo.java +++ b/core/java/android/view/KeyboardShortcutInfo.java @@ -29,7 +29,7 @@ import android.os.Parcelable; */ public final class KeyboardShortcutInfo implements Parcelable { private final CharSequence mLabel; - private final Icon mIcon; + private Icon mIcon; private final char mBaseCharacter; private final int mKeycode; private final int mModifiers; @@ -116,6 +116,15 @@ public final class KeyboardShortcutInfo implements Parcelable { } /** + * Removes an icon that was previously set. + * + * @hide + */ + public void clearIcon() { + mIcon = null; + } + + /** * Returns the base keycode that, combined with the modifiers, triggers this shortcut. If the * base character was set instead, returns {@link KeyEvent#KEYCODE_UNKNOWN}. Valid keycodes are * defined as constants in {@link KeyEvent}. @@ -165,4 +174,4 @@ public final class KeyboardShortcutInfo implements Parcelable { return new KeyboardShortcutInfo[size]; } }; -}
\ No newline at end of file +} diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index 992de22f4265..fdeccfa7b9d0 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -16,8 +16,6 @@ package com.android.externalstorage; -import static java.util.regex.Pattern.CASE_INSENSITIVE; - import android.annotation.NonNull; import android.annotation.Nullable; import android.app.usage.StorageStatsManager; @@ -61,12 +59,15 @@ import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.UUID; -import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * Presents content of the shared (a.k.a. "external") storage. @@ -89,12 +90,9 @@ public class ExternalStorageProvider extends FileSystemProvider { private static final Uri BASE_URI = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build(); - /** - * Regex for detecting {@code /Android/data/}, {@code /Android/obb/} and - * {@code /Android/sandbox/} along with all their subdirectories and content. - */ - private static final Pattern PATTERN_RESTRICTED_ANDROID_SUBTREES = - Pattern.compile("^Android/(?:data|obb|sandbox)(?:/.+)?", CASE_INSENSITIVE); + private static final String PRIMARY_EMULATED_STORAGE_PATH = "/storage/emulated/"; + + private static final String STORAGE_PATH = "/storage/"; private static final String[] DEFAULT_ROOT_PROJECTION = new String[] { Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE, @@ -308,11 +306,70 @@ public class ExternalStorageProvider extends FileSystemProvider { return false; } - final String path = getPathFromDocId(documentId); - return PATTERN_RESTRICTED_ANDROID_SUBTREES.matcher(path).matches(); + try { + final RootInfo root = getRootFromDocId(documentId); + final String canonicalPath = getPathFromDocId(documentId); + return isRestrictedPath(root.rootId, canonicalPath); + } catch (Exception e) { + return true; + } } /** + * Based on the given root id and path, we restrict path access if file is Android/data or + * Android/obb or Android/sandbox or one of their subdirectories. + * + * @param canonicalPath of the file + * @return true if path is restricted + */ + private boolean isRestrictedPath(String rootId, String canonicalPath) { + if (rootId == null || canonicalPath == null) { + return true; + } + + final String rootPath; + if (rootId.equalsIgnoreCase(ROOT_ID_PRIMARY_EMULATED)) { + // Creates "/storage/emulated/<user-id>" + rootPath = PRIMARY_EMULATED_STORAGE_PATH + UserHandle.myUserId(); + } else { + // Creates "/storage/<volume-uuid>" + rootPath = STORAGE_PATH + rootId; + } + List<java.nio.file.Path> restrictedPathList = Arrays.asList( + Paths.get(rootPath, "Android", "data"), + Paths.get(rootPath, "Android", "obb"), + Paths.get(rootPath, "Android", "sandbox")); + // We need to identify restricted parent paths which actually exist on the device + List<java.nio.file.Path> validRestrictedPathsToCheck = restrictedPathList.stream().filter( + Files::exists).collect(Collectors.toList()); + + boolean isRestricted = false; + java.nio.file.Path filePathToCheck = Paths.get(rootPath, canonicalPath); + try { + while (filePathToCheck != null) { + for (java.nio.file.Path restrictedPath : validRestrictedPathsToCheck) { + if (Files.isSameFile(restrictedPath, filePathToCheck)) { + isRestricted = true; + Log.v(TAG, "Restricting access for path: " + filePathToCheck); + break; + } + } + if (isRestricted) { + break; + } + + filePathToCheck = filePathToCheck.getParent(); + } + } catch (Exception e) { + Log.w(TAG, "Error in checking file equality check.", e); + isRestricted = true; + } + + return isRestricted; + } + + + /** * Check that the directory is the root of storage or blocked file from tree. * <p> * Note, that this is different from hidden documents: blocked documents <b>WILL</b> appear diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java index 7e6ddcfea762..cc373d3c8b0f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java @@ -378,6 +378,7 @@ public final class KeyboardShortcuts { @Override public void onKeyboardShortcutsReceived( final List<KeyboardShortcutGroup> result) { + sanitiseShortcuts(result); result.add(getSystemShortcuts()); final KeyboardShortcutGroup appShortcuts = getDefaultApplicationShortcuts(); if (appShortcuts != null) { @@ -388,6 +389,14 @@ public final class KeyboardShortcuts { }, deviceId); } + static void sanitiseShortcuts(List<KeyboardShortcutGroup> shortcutGroups) { + for (KeyboardShortcutGroup group : shortcutGroups) { + for (KeyboardShortcutInfo info : group.getItems()) { + info.clearIcon(); + } + } + } + private void dismissKeyboardShortcuts() { if (mKeyboardShortcutsDialog != null) { mKeyboardShortcutsDialog.dismiss(); diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 7ee11f5182c0..fd73e8c4b16f 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -879,7 +879,7 @@ abstract public class ManagedServices { || isPackageOrComponentAllowed(component.getPackageName(), userId))) { return false; } - return isValidService(component, userId); + return componentHasBindPermission(component, userId); } private boolean componentHasBindPermission(ComponentName component, int userId) { @@ -1229,12 +1229,11 @@ abstract public class ManagedServices { if (TextUtils.equals(getPackageName(approvedPackageOrComponent), packageName)) { final ComponentName component = ComponentName.unflattenFromString( approvedPackageOrComponent); - if (component != null && !isValidService(component, userId)) { + if (component != null && !componentHasBindPermission(component, userId)) { approved.removeAt(j); if (DEBUG) { Slog.v(TAG, "Removing " + approvedPackageOrComponent - + " from approved list; no bind permission or " - + "service interface filter found " + + " from approved list; no bind permission found " + mConfig.bindPermission); } } @@ -1253,11 +1252,6 @@ abstract public class ManagedServices { } } - protected boolean isValidService(ComponentName component, int userId) { - return componentHasBindPermission(component, userId) && queryPackageForServices( - component.getPackageName(), userId).contains(component); - } - protected boolean isValidEntry(String packageOrComponent, int userId) { return hasMatchingServices(packageOrComponent, userId); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index 4ab6ccfb6446..f818725f0067 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -1010,80 +1010,6 @@ public class ManagedServicesTest extends UiServiceTestCase { } @Test - public void testUpgradeAppNoIntentFilterNoRebind() throws Exception { - Context context = spy(getContext()); - doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any()); - - ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, - mIpm, APPROVAL_BY_COMPONENT); - - List<String> packages = new ArrayList<>(); - packages.add("package"); - addExpectedServices(service, packages, 0); - - final ComponentName unapprovedComponent = ComponentName.unflattenFromString("package/C1"); - final ComponentName approvedComponent = ComponentName.unflattenFromString("package/C2"); - - // Both components are approved initially - mExpectedPrimaryComponentNames.clear(); - mExpectedPrimaryPackages.clear(); - mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2"); - mExpectedSecondaryComponentNames.clear(); - mExpectedSecondaryPackages.clear(); - - loadXml(service); - - //Components keep bind permission - when(mIpm.getServiceInfo(any(), anyInt(), anyInt())).thenAnswer( - (Answer<ServiceInfo>) invocation -> { - ComponentName invocationCn = invocation.getArgument(0); - if (invocationCn != null) { - ServiceInfo serviceInfo = new ServiceInfo(); - serviceInfo.packageName = invocationCn.getPackageName(); - serviceInfo.name = invocationCn.getClassName(); - serviceInfo.permission = service.getConfig().bindPermission; - serviceInfo.metaData = null; - return serviceInfo; - } - return null; - } - ); - - //Component package/C1 loses serviceInterface intent filter - ManagedServices.Config config = service.getConfig(); - when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())). - thenAnswer(new Answer<List<ResolveInfo>>() { - @Override - public List<ResolveInfo> answer(InvocationOnMock invocationOnMock) - throws Throwable { - Object[] args = invocationOnMock.getArguments(); - Intent invocationIntent = (Intent) args[0]; - if (invocationIntent != null) { - if (invocationIntent.getAction().equals(config.serviceInterface) - && packages.contains(invocationIntent.getPackage())) { - List<ResolveInfo> dummyServices = new ArrayList<>(); - ResolveInfo resolveInfo = new ResolveInfo(); - ServiceInfo serviceInfo = new ServiceInfo(); - serviceInfo.packageName = invocationIntent.getPackage(); - serviceInfo.name = approvedComponent.getClassName(); - serviceInfo.permission = service.getConfig().bindPermission; - resolveInfo.serviceInfo = serviceInfo; - dummyServices.add(resolveInfo); - return dummyServices; - } - } - return new ArrayList<>(); - } - }); - - // Trigger package update - service.onPackagesChanged(false, new String[]{"package"}, new int[]{0}); - - assertFalse(service.isComponentEnabledForCurrentProfiles(unapprovedComponent)); - assertTrue(service.isComponentEnabledForCurrentProfiles(approvedComponent)); - } - - @Test public void testSetPackageOrComponentEnabled() throws Exception { for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) { ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, |