diff options
16 files changed, 543 insertions, 181 deletions
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index fce23cf6819a..78c75bc61102 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -272,6 +272,7 @@ public abstract class Context { BIND_IMPORTANT, BIND_ADJUST_WITH_ACTIVITY, BIND_NOT_PERCEPTIBLE, + BIND_DENY_ACTIVITY_STARTS_PRE_34, BIND_INCLUDE_CAPABILITIES }) @Retention(RetentionPolicy.SOURCE) @@ -393,6 +394,14 @@ public abstract class Context { /*********** Hidden flags below this line ***********/ /** + * Flag for {@link #bindService}: If binding from an app that is visible, the bound service is + * allowed to start an activity from background. Add a flag so that this behavior can be opted + * out. + * @hide + */ + public static final int BIND_DENY_ACTIVITY_STARTS_PRE_34 = 0X000004000; + + /** * Flag for {@link #bindService}: This flag is only intended to be used by the system to * indicate that a service binding is not considered as real package component usage and should * not generate a {@link android.app.usage.UsageEvents.Event#APP_COMPONENT_USED} event in usage diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 2d461c6cf92e..d380522de643 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -1192,8 +1192,17 @@ public class DreamService extends Service implements Window.Callback { if (!flattenedString.contains("/")) { return new ComponentName(serviceInfo.packageName, flattenedString); } - - return ComponentName.unflattenFromString(flattenedString); + // Ensure that the component is from the same package as the dream service. If not, + // treat the component as invalid and return null instead. + final ComponentName cn = ComponentName.unflattenFromString(flattenedString); + if (cn == null) return null; + if (!cn.getPackageName().equals(serviceInfo.packageName)) { + Log.w(TAG, + "Inconsistent package name in component: " + cn.getPackageName() + + ", should be: " + serviceInfo.packageName); + return null; + } + return cn; } /** diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java index 563cf0414ab9..be944d6410e8 100644 --- a/core/java/com/android/internal/content/FileSystemProvider.java +++ b/core/java/com/android/internal/content/FileSystemProvider.java @@ -62,14 +62,14 @@ import java.nio.file.FileVisitor; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayDeque; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Locale; +import java.util.Queue; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.function.Predicate; -import java.util.regex.Pattern; /** * A helper class for {@link android.provider.DocumentsProvider} to perform file operations on local @@ -87,6 +87,8 @@ public abstract class FileSystemProvider extends DocumentsProvider { DocumentsContract.QUERY_ARG_LAST_MODIFIED_AFTER, DocumentsContract.QUERY_ARG_MIME_TYPES); + private static final int MAX_RESULTS_NUMBER = 23; + private static String joinNewline(String... args) { return TextUtils.join("\n", args); } @@ -373,62 +375,53 @@ public abstract class FileSystemProvider extends DocumentsProvider { } /** - * This method is similar to - * {@link DocumentsProvider#queryChildDocuments(String, String[], String)}. This method returns - * all children documents including hidden directories/files. - * - * <p> - * In a scoped storage world, access to "Android/data" style directories are hidden for privacy - * reasons. This method may show privacy sensitive data, so its usage should only be in - * restricted modes. - * - * @param parentDocumentId the directory to return children for. - * @param projection list of {@link Document} columns to put into the - * cursor. If {@code null} all supported columns should be - * included. - * @param sortOrder how to order the rows, formatted as an SQL - * {@code ORDER BY} clause (excluding the ORDER BY itself). - * Passing {@code null} will use the default sort order, which - * may be unordered. This ordering is a hint that can be used to - * prioritize how data is fetched from the network, but UI may - * always enforce a specific ordering - * @throws FileNotFoundException when parent document doesn't exist or query fails + * WARNING: this method should really be {@code final}, but for the backward compatibility it's + * not; new classes that extend {@link FileSystemProvider} should override + * {@link #queryChildDocuments(String, String[], String, boolean)}, not this method. */ - protected Cursor queryChildDocumentsShowAll( - String parentDocumentId, String[] projection, String sortOrder) + @Override + public Cursor queryChildDocuments(String documentId, String[] projection, String sortOrder) throws FileNotFoundException { - return queryChildDocuments(parentDocumentId, projection, sortOrder, File -> true); + return queryChildDocuments(documentId, projection, sortOrder, /* includeHidden */ false); } + /** + * This method is similar to {@link #queryChildDocuments(String, String[], String)}, however, it + * could return <b>all</b> content of the directory, <b>including restricted (hidden) + * directories and files</b>. + * <p> + * In the scoped storage world, some directories and files (e.g. {@code Android/data/} and + * {@code Android/obb/} on the external storage) are hidden for privacy reasons. + * Hence, this method may reveal privacy-sensitive data, thus should be used with extra care. + */ @Override - public Cursor queryChildDocuments( - String parentDocumentId, String[] projection, String sortOrder) - throws FileNotFoundException { - // Access to some directories is hidden for privacy reasons. - return queryChildDocuments(parentDocumentId, projection, sortOrder, this::shouldShow); + public final Cursor queryChildDocumentsForManage(String documentId, String[] projection, + String sortOrder) throws FileNotFoundException { + return queryChildDocuments(documentId, projection, sortOrder, /* includeHidden */ true); } - private Cursor queryChildDocuments( - String parentDocumentId, String[] projection, String sortOrder, - @NonNull Predicate<File> filter) throws FileNotFoundException { - final File parent = getFileForDocId(parentDocumentId); + protected Cursor queryChildDocuments(String documentId, String[] projection, String sortOrder, + boolean includeHidden) throws FileNotFoundException { + final File parent = getFileForDocId(documentId); final MatrixCursor result = new DirectoryCursor( - resolveProjection(projection), parentDocumentId, parent); + resolveProjection(projection), documentId, parent); + + if (!parent.isDirectory()) { + Log.w(TAG, '"' + documentId + "\" is not a directory"); + return result; + } - if (!filter.test(parent)) { - Log.w(TAG, "No permission to access parentDocumentId: " + parentDocumentId); + if (!includeHidden && shouldHideDocument(documentId)) { + Log.w(TAG, "Queried directory \"" + documentId + "\" is hidden"); return result; } - if (parent.isDirectory()) { - for (File file : FileUtils.listFilesOrEmpty(parent)) { - if (filter.test(file)) { - includeFile(result, null, file); - } - } - } else { - Log.w(TAG, "parentDocumentId '" + parentDocumentId + "' is not Directory"); + for (File file : FileUtils.listFilesOrEmpty(parent)) { + if (!includeHidden && shouldHideDocument(file)) continue; + + includeFile(result, null, file); } + return result; } @@ -450,23 +443,29 @@ public abstract class FileSystemProvider extends DocumentsProvider { * * @see ContentResolver#EXTRA_HONORED_ARGS */ - protected final Cursor querySearchDocuments( - File folder, String[] projection, Set<String> exclusion, Bundle queryArgs) - throws FileNotFoundException { + protected final Cursor querySearchDocuments(File folder, String[] projection, + Set<String> exclusion, Bundle queryArgs) throws FileNotFoundException { final MatrixCursor result = new MatrixCursor(resolveProjection(projection)); - final LinkedList<File> pending = new LinkedList<>(); - pending.add(folder); - while (!pending.isEmpty() && result.getCount() < 24) { - final File file = pending.removeFirst(); - if (shouldHide(file)) continue; + + // We'll be a running a BFS here. + final Queue<File> pending = new ArrayDeque<>(); + pending.offer(folder); + + while (!pending.isEmpty() && result.getCount() < MAX_RESULTS_NUMBER) { + final File file = pending.poll(); + + // Skip hidden documents (both files and directories) + if (shouldHideDocument(file)) continue; if (file.isDirectory()) { for (File child : FileUtils.listFilesOrEmpty(file)) { - pending.add(child); + pending.offer(child); } } - if (!exclusion.contains(file.getAbsolutePath()) && matchSearchQueryArguments(file, - queryArgs)) { + + if (exclusion.contains(file.getAbsolutePath())) continue; + + if (matchSearchQueryArguments(file, queryArgs)) { includeFile(result, null, file); } } @@ -610,26 +609,23 @@ public abstract class FileSystemProvider extends DocumentsProvider { final int flagIndex = ArrayUtils.indexOf(columns, Document.COLUMN_FLAGS); if (flagIndex != -1) { + final boolean isDir = mimeType.equals(Document.MIME_TYPE_DIR); int flags = 0; if (file.canWrite()) { - if (mimeType.equals(Document.MIME_TYPE_DIR)) { + flags |= Document.FLAG_SUPPORTS_DELETE; + flags |= Document.FLAG_SUPPORTS_RENAME; + flags |= Document.FLAG_SUPPORTS_MOVE; + if (isDir) { flags |= Document.FLAG_DIR_SUPPORTS_CREATE; - flags |= Document.FLAG_SUPPORTS_DELETE; - flags |= Document.FLAG_SUPPORTS_RENAME; - flags |= Document.FLAG_SUPPORTS_MOVE; - - if (shouldBlockFromTree(docId)) { - flags |= Document.FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE; - } - } else { flags |= Document.FLAG_SUPPORTS_WRITE; - flags |= Document.FLAG_SUPPORTS_DELETE; - flags |= Document.FLAG_SUPPORTS_RENAME; - flags |= Document.FLAG_SUPPORTS_MOVE; } } + if (isDir && shouldBlockDirectoryFromTree(docId)) { + flags |= Document.FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE; + } + if (mimeType.startsWith("image/")) { flags |= Document.FLAG_SUPPORTS_THUMBNAIL; } @@ -662,22 +658,36 @@ public abstract class FileSystemProvider extends DocumentsProvider { return row; } - private static final Pattern PATTERN_HIDDEN_PATH = Pattern.compile( - "(?i)^/storage/[^/]+/(?:[0-9]+/)?Android/(?:data|obb|sandbox)$"); - /** - * In a scoped storage world, access to "Android/data" style directories are - * hidden for privacy reasons. + * Some providers may want to restrict access to certain directories and files, + * e.g. <i>"Android/data"</i> and <i>"Android/obb"</i> on the shared storage for + * privacy reasons. + * Such providers should override this method. */ - protected boolean shouldHide(@NonNull File file) { - return (PATTERN_HIDDEN_PATH.matcher(file.getAbsolutePath()).matches()); + protected boolean shouldHideDocument(@NonNull String documentId) + throws FileNotFoundException { + return false; } - private boolean shouldShow(@NonNull File file) { - return !shouldHide(file); + /** + * A variant of the {@link #shouldHideDocument(String)} that takes a {@link File} instead of + * a {@link String} {@code documentId}. + * + * @see #shouldHideDocument(String) + */ + protected final boolean shouldHideDocument(@NonNull File document) + throws FileNotFoundException { + return shouldHideDocument(getDocIdForFile(document)); } - protected boolean shouldBlockFromTree(@NonNull String docId) { + /** + * @return if the directory that should be blocked from being selected when the user launches + * an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} intent. + * + * @see Document#FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE + */ + protected boolean shouldBlockDirectoryFromTree(@NonNull String documentId) + throws FileNotFoundException { return false; } diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto index 7205dd817eb5..305b7661aadc 100644 --- a/core/proto/android/server/activitymanagerservice.proto +++ b/core/proto/android/server/activitymanagerservice.proto @@ -524,6 +524,7 @@ message ConnectionRecordProto { DEAD = 15; NOT_PERCEPTIBLE = 16; INCLUDE_CAPABILITIES = 17; + DENY_ACTIVITY_STARTS_PRE_34 = 18; } repeated Flag flags = 3; optional string service_name = 4; diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index 4c313b22f71e..3409c29d3c2c 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -16,6 +16,8 @@ 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; @@ -64,7 +66,19 @@ import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.UUID; - +import java.util.regex.Pattern; + +/** + * Presents content of the shared (a.k.a. "external") storage. + * <p> + * Starting with Android 11 (R), restricts access to the certain sections of the shared storage: + * {@code Android/data/}, {@code Android/obb/} and {@code Android/sandbox/}, that will be hidden in + * the DocumentsUI by default. + * See <a href="https://developer.android.com/about/versions/11/privacy/storage"> + * Storage updates in Android 11</a>. + * <p> + * Documents ID format: {@code root:path/to/file}. + */ public class ExternalStorageProvider extends FileSystemProvider { private static final String TAG = "ExternalStorage"; @@ -75,7 +89,12 @@ public class ExternalStorageProvider extends FileSystemProvider { private static final Uri BASE_URI = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build(); - // docId format: root:path/to/file + /** + * 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[] DEFAULT_ROOT_PROJECTION = new String[] { Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE, @@ -278,76 +297,91 @@ public class ExternalStorageProvider extends FileSystemProvider { return projection != null ? projection : DEFAULT_ROOT_PROJECTION; } + /** + * Mark {@code Android/data/}, {@code Android/obb/} and {@code Android/sandbox/} on the + * integrated shared ("external") storage along with all their content and subdirectories as + * hidden. + */ @Override - public Cursor queryChildDocumentsForManage( - String parentDocId, String[] projection, String sortOrder) - throws FileNotFoundException { - return queryChildDocumentsShowAll(parentDocId, projection, sortOrder); + protected boolean shouldHideDocument(@NonNull String documentId) { + // Don't need to hide anything on USB drives. + if (isOnRemovableUsbStorage(documentId)) { + return false; + } + + final String path = getPathFromDocId(documentId); + return PATTERN_RESTRICTED_ANDROID_SUBTREES.matcher(path).matches(); } /** * 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 + * the UI, but the user <b>WILL NOT</b> be able to select them. * - * @param docId the docId of the directory to be checked + * @param documentId the docId of the directory to be checked * @return true, should be blocked from tree. Otherwise, false. + * + * @see Document#FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE */ @Override - protected boolean shouldBlockFromTree(@NonNull String docId) { - try { - final File dir = getFileForDocId(docId, false /* visible */); - - // the file is null or it is not a directory - if (dir == null || !dir.isDirectory()) { - return false; - } + protected boolean shouldBlockDirectoryFromTree(@NonNull String documentId) + throws FileNotFoundException { + final File dir = getFileForDocId(documentId, false); + // The file is null or it is not a directory + if (dir == null || !dir.isDirectory()) { + return false; + } - // Allow all directories on USB, including the root. - try { - RootInfo rootInfo = getRootFromDocId(docId); - if ((rootInfo.flags & Root.FLAG_REMOVABLE_USB) == Root.FLAG_REMOVABLE_USB) { - return false; - } - } catch (FileNotFoundException e) { - Log.e(TAG, "Failed to determine rootInfo for docId"); - } + // Allow all directories on USB, including the root. + if (isOnRemovableUsbStorage(documentId)) { + return false; + } - final String path = getPathFromDocId(docId); + // Get canonical(!) path. Note that this path will have neither leading nor training "/". + // This the root's path will be just an empty string. + final String path = getPathFromDocId(documentId); - // Block the root of the storage - if (path.isEmpty()) { - return true; - } + // Block the root of the storage + if (path.isEmpty()) { + return true; + } - // Block Download folder from tree - if (TextUtils.equals(Environment.DIRECTORY_DOWNLOADS.toLowerCase(Locale.ROOT), - path.toLowerCase(Locale.ROOT))) { - return true; - } + // Block /Download/ and /Android/ folders from the tree. + if (equalIgnoringCase(path, Environment.DIRECTORY_DOWNLOADS) || + equalIgnoringCase(path, Environment.DIRECTORY_ANDROID)) { + return true; + } - // Block /Android - if (TextUtils.equals(Environment.DIRECTORY_ANDROID.toLowerCase(Locale.ROOT), - path.toLowerCase(Locale.ROOT))) { - return true; - } + // This shouldn't really make a difference, but just in case - let's block hidden + // directories as well. + if (shouldHideDocument(documentId)) { + return true; + } - // Block /Android/data, /Android/obb, /Android/sandbox and sub dirs - if (shouldHide(dir)) { - return true; - } + return false; + } + private boolean isOnRemovableUsbStorage(@NonNull String documentId) { + final RootInfo rootInfo; + try { + rootInfo = getRootFromDocId(documentId); + } catch (FileNotFoundException e) { + Log.e(TAG, "Failed to determine rootInfo for docId\"" + documentId + '"'); return false; - } catch (IOException e) { - throw new IllegalArgumentException( - "Failed to determine if " + docId + " should block from tree " + ": " + e); } + + return (rootInfo.flags & Root.FLAG_REMOVABLE_USB) != 0; } + @NonNull @Override - protected String getDocIdForFile(File file) throws FileNotFoundException { + protected String getDocIdForFile(@NonNull File file) throws FileNotFoundException { return getDocIdForFileMaybeCreate(file, false); } - private String getDocIdForFileMaybeCreate(File file, boolean createNewDir) + @NonNull + private String getDocIdForFileMaybeCreate(@NonNull File file, boolean createNewDir) throws FileNotFoundException { String path = file.getAbsolutePath(); @@ -417,31 +451,33 @@ public class ExternalStorageProvider extends FileSystemProvider { private File getFileForDocId(String docId, boolean visible, boolean mustExist) throws FileNotFoundException { RootInfo root = getRootFromDocId(docId); - return buildFile(root, docId, visible, mustExist); + return buildFile(root, docId, mustExist); } - private Pair<RootInfo, File> resolveDocId(String docId, boolean visible) - throws FileNotFoundException { + private Pair<RootInfo, File> resolveDocId(String docId) throws FileNotFoundException { RootInfo root = getRootFromDocId(docId); - return Pair.create(root, buildFile(root, docId, visible, true)); + return Pair.create(root, buildFile(root, docId, /* mustExist */ true)); } @VisibleForTesting - static String getPathFromDocId(String docId) throws IOException { + static String getPathFromDocId(String docId) { final int splitIndex = docId.indexOf(':', 1); final String docIdPath = docId.substring(splitIndex + 1); - // Get CanonicalPath and remove the first "/" - final String canonicalPath = new File(docIdPath).getCanonicalPath().substring(1); - if (canonicalPath.isEmpty()) { - return canonicalPath; + // Canonicalize path and strip the leading "/" + final String path; + try { + path = new File(docIdPath).getCanonicalPath().substring(1); + } catch (IOException e) { + Log.w(TAG, "Could not canonicalize \"" + docIdPath + '"'); + return ""; } - // remove trailing "/" - if (canonicalPath.charAt(canonicalPath.length() - 1) == '/') { - return canonicalPath.substring(0, canonicalPath.length() - 1); + // Remove the trailing "/" as well. + if (!path.isEmpty() && path.charAt(path.length() - 1) == '/') { + return path.substring(0, path.length() - 1); } else { - return canonicalPath; + return path; } } @@ -460,7 +496,7 @@ public class ExternalStorageProvider extends FileSystemProvider { return root; } - private File buildFile(RootInfo root, String docId, boolean visible, boolean mustExist) + private File buildFile(RootInfo root, String docId, boolean mustExist) throws FileNotFoundException { final int splitIndex = docId.indexOf(':', 1); final String path = docId.substring(splitIndex + 1); @@ -544,7 +580,7 @@ public class ExternalStorageProvider extends FileSystemProvider { @Override public Path findDocumentPath(@Nullable String parentDocId, String childDocId) throws FileNotFoundException { - final Pair<RootInfo, File> resolvedDocId = resolveDocId(childDocId, false); + final Pair<RootInfo, File> resolvedDocId = resolveDocId(childDocId); final RootInfo root = resolvedDocId.first; File child = resolvedDocId.second; @@ -648,6 +684,13 @@ public class ExternalStorageProvider extends FileSystemProvider { } } + /** + * Print the state into the given stream. + * Gets invoked when you run: + * <pre> + * adb shell dumpsys activity provider com.android.externalstorage/.ExternalStorageProvider + * </pre> + */ @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 160); @@ -731,4 +774,8 @@ public class ExternalStorageProvider extends FileSystemProvider { } return bundle; } + + private static boolean equalIgnoringCase(@NonNull String a, @NonNull String b) { + return TextUtils.equals(a.toLowerCase(Locale.ROOT), b.toLowerCase(Locale.ROOT)); + } } diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 6b859763eb6f..c6c08585640f 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -51,6 +51,7 @@ import com.android.systemui.util.time.SystemClock; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; import javax.inject.Inject; @@ -144,6 +145,10 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon protected void setListening(boolean listening) { mListening = listening; if (listening) { + // System UI could be restarted while ops are active, so fetch the currently active ops + // once System UI starts listening again. + fetchCurrentActiveOps(); + mAppOps.startWatchingActive(OPS, this); mAppOps.startWatchingNoted(OPS, this); mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler); @@ -176,6 +181,29 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon } } + private void fetchCurrentActiveOps() { + List<AppOpsManager.PackageOps> packageOps = mAppOps.getPackagesForOps(OPS); + for (AppOpsManager.PackageOps op : packageOps) { + for (AppOpsManager.OpEntry entry : op.getOps()) { + for (Map.Entry<String, AppOpsManager.AttributedOpEntry> attributedOpEntry : + entry.getAttributedOpEntries().entrySet()) { + if (attributedOpEntry.getValue().isRunning()) { + onOpActiveChanged( + entry.getOpStr(), + op.getUid(), + op.getPackageName(), + /* attributionTag= */ attributedOpEntry.getKey(), + /* active= */ true, + // AppOpsManager doesn't have a way to fetch attribution flags or + // chain ID given an op entry, so default them to none. + AppOpsManager.ATTRIBUTION_FLAGS_NONE, + AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE); + } + } + } + } + } + /** * Adds a callback that will get notifified when an AppOp of the type the controller tracks * changes diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt index 1e7fc93cb9fa..a48e13c1a57f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt @@ -88,8 +88,9 @@ class SystemStatusAnimationScheduler @Inject constructor( } fun onStatusEvent(event: StatusEvent) { - // Ignore any updates until the system is up and running - if (isTooEarly() || !isImmersiveIndicatorEnabled()) { + // Ignore any updates until the system is up and running. However, for important events that + // request to be force visible (like privacy), ignore whether it's too early. + if ((isTooEarly() && !event.forceVisible) || !isImmersiveIndicatorEnabled()) { return } diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index 61a651234e0c..e6c36c18342c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -19,6 +19,8 @@ package com.android.systemui.appops; import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.TestCase.assertFalse; import static org.junit.Assert.assertEquals; @@ -66,6 +68,7 @@ import org.mockito.MockitoAnnotations; import java.util.Collections; import java.util.List; +import java.util.Map; @SmallTest @RunWith(AndroidTestingRunner.class) @@ -158,6 +161,204 @@ public class AppOpsControllerTest extends SysuiTestCase { } @Test + public void startListening_fetchesCurrentActive_none() { + when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS)) + .thenReturn(List.of()); + + mController.setListening(true); + + assertThat(mController.getActiveAppOps()).isEmpty(); + } + + /** Regression test for b/294104969. */ + @Test + public void startListening_fetchesCurrentActive_oneActive() { + AppOpsManager.PackageOps packageOps = createPackageOp( + "package.test", + /* packageUid= */ 2, + AppOpsManager.OPSTR_FINE_LOCATION, + /* isRunning= */ true); + when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS)) + .thenReturn(List.of(packageOps)); + + // WHEN we start listening + mController.setListening(true); + + // THEN the active list has the op + List<AppOpItem> list = mController.getActiveAppOps(); + assertEquals(1, list.size()); + AppOpItem first = list.get(0); + assertThat(first.getPackageName()).isEqualTo("package.test"); + assertThat(first.getUid()).isEqualTo(2); + assertThat(first.getCode()).isEqualTo(AppOpsManager.OP_FINE_LOCATION); + } + + @Test + public void startListening_fetchesCurrentActive_multiplePackages() { + AppOpsManager.PackageOps packageOps1 = createPackageOp( + "package.one", + /* packageUid= */ 1, + AppOpsManager.OPSTR_FINE_LOCATION, + /* isRunning= */ true); + AppOpsManager.PackageOps packageOps2 = createPackageOp( + "package.two", + /* packageUid= */ 2, + AppOpsManager.OPSTR_FINE_LOCATION, + /* isRunning= */ false); + AppOpsManager.PackageOps packageOps3 = createPackageOp( + "package.three", + /* packageUid= */ 3, + AppOpsManager.OPSTR_FINE_LOCATION, + /* isRunning= */ true); + when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS)) + .thenReturn(List.of(packageOps1, packageOps2, packageOps3)); + + // WHEN we start listening + mController.setListening(true); + + // THEN the active list has the ops + List<AppOpItem> list = mController.getActiveAppOps(); + assertEquals(2, list.size()); + + AppOpItem item0 = list.get(0); + assertThat(item0.getPackageName()).isEqualTo("package.one"); + assertThat(item0.getUid()).isEqualTo(1); + assertThat(item0.getCode()).isEqualTo(AppOpsManager.OP_FINE_LOCATION); + + AppOpItem item1 = list.get(1); + assertThat(item1.getPackageName()).isEqualTo("package.three"); + assertThat(item1.getUid()).isEqualTo(3); + assertThat(item1.getCode()).isEqualTo(AppOpsManager.OP_FINE_LOCATION); + } + + @Test + public void startListening_fetchesCurrentActive_multipleEntries() { + AppOpsManager.PackageOps packageOps = mock(AppOpsManager.PackageOps.class); + when(packageOps.getUid()).thenReturn(1); + when(packageOps.getPackageName()).thenReturn("package.one"); + + // Entry 1 + AppOpsManager.OpEntry entry1 = mock(AppOpsManager.OpEntry.class); + when(entry1.getOpStr()).thenReturn(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE); + AppOpsManager.AttributedOpEntry attributed1 = mock(AppOpsManager.AttributedOpEntry.class); + when(attributed1.isRunning()).thenReturn(true); + when(entry1.getAttributedOpEntries()).thenReturn(Map.of("tag", attributed1)); + // Entry 2 + AppOpsManager.OpEntry entry2 = mock(AppOpsManager.OpEntry.class); + when(entry2.getOpStr()).thenReturn(AppOpsManager.OPSTR_CAMERA); + AppOpsManager.AttributedOpEntry attributed2 = mock(AppOpsManager.AttributedOpEntry.class); + when(attributed2.isRunning()).thenReturn(true); + when(entry2.getAttributedOpEntries()).thenReturn(Map.of("tag", attributed2)); + // Entry 3 + AppOpsManager.OpEntry entry3 = mock(AppOpsManager.OpEntry.class); + when(entry3.getOpStr()).thenReturn(AppOpsManager.OPSTR_FINE_LOCATION); + AppOpsManager.AttributedOpEntry attributed3 = mock(AppOpsManager.AttributedOpEntry.class); + when(attributed3.isRunning()).thenReturn(false); + when(entry3.getAttributedOpEntries()).thenReturn(Map.of("tag", attributed3)); + + when(packageOps.getOps()).thenReturn(List.of(entry1, entry2, entry3)); + when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS)) + .thenReturn(List.of(packageOps)); + + // WHEN we start listening + mController.setListening(true); + + // THEN the active list has the ops + List<AppOpItem> list = mController.getActiveAppOps(); + assertEquals(2, list.size()); + + AppOpItem first = list.get(0); + assertThat(first.getPackageName()).isEqualTo("package.one"); + assertThat(first.getUid()).isEqualTo(1); + assertThat(first.getCode()).isEqualTo(AppOpsManager.OP_PHONE_CALL_MICROPHONE); + + AppOpItem second = list.get(1); + assertThat(second.getPackageName()).isEqualTo("package.one"); + assertThat(second.getUid()).isEqualTo(1); + assertThat(second.getCode()).isEqualTo(AppOpsManager.OP_CAMERA); + } + + @Test + public void startListening_fetchesCurrentActive_multipleAttributes() { + AppOpsManager.PackageOps packageOps = mock(AppOpsManager.PackageOps.class); + when(packageOps.getUid()).thenReturn(1); + when(packageOps.getPackageName()).thenReturn("package.one"); + AppOpsManager.OpEntry entry = mock(AppOpsManager.OpEntry.class); + when(entry.getOpStr()).thenReturn(AppOpsManager.OPSTR_RECORD_AUDIO); + + AppOpsManager.AttributedOpEntry attributed1 = mock(AppOpsManager.AttributedOpEntry.class); + when(attributed1.isRunning()).thenReturn(false); + AppOpsManager.AttributedOpEntry attributed2 = mock(AppOpsManager.AttributedOpEntry.class); + when(attributed2.isRunning()).thenReturn(true); + AppOpsManager.AttributedOpEntry attributed3 = mock(AppOpsManager.AttributedOpEntry.class); + when(attributed3.isRunning()).thenReturn(true); + when(entry.getAttributedOpEntries()).thenReturn( + Map.of("attr1", attributed1, "attr2", attributed2, "attr3", attributed3)); + + when(packageOps.getOps()).thenReturn(List.of(entry)); + when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS)) + .thenReturn(List.of(packageOps)); + + // WHEN we start listening + mController.setListening(true); + + // THEN the active list has the ops + List<AppOpItem> list = mController.getActiveAppOps(); + // Multiple attributes get merged into one entry in the active ops + assertEquals(1, list.size()); + + AppOpItem first = list.get(0); + assertThat(first.getPackageName()).isEqualTo("package.one"); + assertThat(first.getUid()).isEqualTo(1); + assertThat(first.getCode()).isEqualTo(AppOpsManager.OP_RECORD_AUDIO); + } + + /** Regression test for b/294104969. */ + @Test + public void addCallback_existingCallbacksNotifiedOfCurrentActive() { + AppOpsManager.PackageOps packageOps1 = createPackageOp( + "package.one", + /* packageUid= */ 1, + AppOpsManager.OPSTR_FINE_LOCATION, + /* isRunning= */ true); + AppOpsManager.PackageOps packageOps2 = createPackageOp( + "package.two", + /* packageUid= */ 2, + AppOpsManager.OPSTR_RECORD_AUDIO, + /* isRunning= */ true); + AppOpsManager.PackageOps packageOps3 = createPackageOp( + "package.three", + /* packageUid= */ 3, + AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, + /* isRunning= */ true); + when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS)) + .thenReturn(List.of(packageOps1, packageOps2, packageOps3)); + + // WHEN we start listening + mController.addCallback( + new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION}, + mCallback); + mTestableLooper.processAllMessages(); + + // THEN the callback is notified of the current active ops it cares about + verify(mCallback).onActiveStateChanged( + AppOpsManager.OP_FINE_LOCATION, + /* uid= */ 1, + "package.one", + true); + verify(mCallback).onActiveStateChanged( + AppOpsManager.OP_RECORD_AUDIO, + /* uid= */ 2, + "package.two", + true); + verify(mCallback, never()).onActiveStateChanged( + AppOpsManager.OP_PHONE_CALL_MICROPHONE, + /* uid= */ 3, + "package.three", + true); + } + + @Test public void addCallback_includedCode() { mController.addCallback( new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION}, @@ -673,6 +874,22 @@ public class AppOpsControllerTest extends SysuiTestCase { assertEquals(AppOpsManager.OP_PHONE_CALL_CAMERA, list.get(cameraIdx).getCode()); } + private AppOpsManager.PackageOps createPackageOp( + String packageName, int packageUid, String opStr, boolean isRunning) { + AppOpsManager.PackageOps packageOps = mock(AppOpsManager.PackageOps.class); + when(packageOps.getPackageName()).thenReturn(packageName); + when(packageOps.getUid()).thenReturn(packageUid); + AppOpsManager.OpEntry entry = mock(AppOpsManager.OpEntry.class); + when(entry.getOpStr()).thenReturn(opStr); + AppOpsManager.AttributedOpEntry attributed = mock(AppOpsManager.AttributedOpEntry.class); + when(attributed.isRunning()).thenReturn(isRunning); + + when(packageOps.getOps()).thenReturn(Collections.singletonList(entry)); + when(entry.getAttributedOpEntries()).thenReturn(Map.of("tag", attributed)); + + return packageOps; + } + private class TestHandler extends AppOpsControllerImpl.H { TestHandler(Looper looper) { mController.super(looper); diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 3ac7b8e6668e..5f6211f158c2 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -543,8 +543,7 @@ public class CompanionDeviceManagerService extends SystemService { public PendingIntent requestNotificationAccess(ComponentName component, int userId) throws RemoteException { String callingPackage = component.getPackageName(); - checkCanCallNotificationApi(callingPackage); - // TODO: check userId. + checkCanCallNotificationApi(callingPackage, userId); if (component.flattenToString().length() > MAX_CN_LENGTH) { throw new IllegalArgumentException("Component name is too long."); } @@ -570,7 +569,7 @@ public class CompanionDeviceManagerService extends SystemService { @Deprecated @Override public boolean hasNotificationAccess(ComponentName component) throws RemoteException { - checkCanCallNotificationApi(component.getPackageName()); + checkCanCallNotificationApi(component.getPackageName(), getCallingUserId()); NotificationManager nm = getContext().getSystemService(NotificationManager.class); return nm.isNotificationListenerAccessGranted(component); } @@ -727,8 +726,7 @@ public class CompanionDeviceManagerService extends SystemService { legacyCreateAssociation(userId, macAddress, packageName, null); } - private void checkCanCallNotificationApi(String callingPackage) { - final int userId = getCallingUserId(); + private void checkCanCallNotificationApi(String callingPackage, int userId) { enforceCallerIsSystemOr(userId, callingPackage); if (getCallingUid() == SYSTEM_UID) return; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 5f5912bf2143..b75e845e721c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -5439,7 +5439,20 @@ public class ActivityManagerService extends IActivityManager.Stub intent = new Intent(Intent.ACTION_MAIN); } try { - target.send(code, intent, resolvedType, allowlistToken, null, + if (allowlistToken != null) { + final int callingUid = Binder.getCallingUid(); + final String packageName; + final long token = Binder.clearCallingIdentity(); + try { + packageName = AppGlobals.getPackageManager().getNameForUid(callingUid); + } finally { + Binder.restoreCallingIdentity(token); + } + Slog.wtf(TAG, "Send a non-null allowlistToken to a non-PI target." + + " Calling package: " + packageName + "; intent: " + intent + + "; options: " + options); + } + target.send(code, intent, resolvedType, null, null, requiredPermission, options); } catch (RemoteException e) { } diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java index 17fff919ba20..89ada49d7561 100644 --- a/services/core/java/com/android/server/am/ConnectionRecord.java +++ b/services/core/java/com/android/server/am/ConnectionRecord.java @@ -77,6 +77,7 @@ final class ConnectionRecord { Context.BIND_NOT_VISIBLE, Context.BIND_NOT_PERCEPTIBLE, Context.BIND_INCLUDE_CAPABILITIES, + Context.BIND_DENY_ACTIVITY_STARTS_PRE_34, }; private static final int[] BIND_PROTO_ENUMS = new int[] { ConnectionRecordProto.AUTO_CREATE, @@ -96,6 +97,7 @@ final class ConnectionRecord { ConnectionRecordProto.NOT_VISIBLE, ConnectionRecordProto.NOT_PERCEPTIBLE, ConnectionRecordProto.INCLUDE_CAPABILITIES, + ConnectionRecordProto.DENY_ACTIVITY_STARTS_PRE_34, }; void dump(PrintWriter pw, String prefix) { @@ -237,6 +239,9 @@ final class ConnectionRecord { if ((flags & Context.BIND_NOT_PERCEPTIBLE) != 0) { sb.append("!PRCP "); } + if ((flags & Context.BIND_DENY_ACTIVITY_STARTS_PRE_34) != 0) { + sb.append("BALFD "); + } if ((flags & Context.BIND_INCLUDE_CAPABILITIES) != 0) { sb.append("CAPS "); } diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java index 9951e983a752..bf27a3100050 100644 --- a/services/core/java/com/android/server/am/ProcessServiceRecord.java +++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java @@ -27,6 +27,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import com.android.internal.annotations.GuardedBy; +import com.android.server.wm.WindowProcessController; import java.io.PrintWriter; import java.util.ArrayList; @@ -414,19 +415,21 @@ final class ProcessServiceRecord { return mConnections.size(); } - void addBoundClientUid(int clientUid) { + void addBoundClientUid(int clientUid, String clientPackageName, int bindFlags) { mBoundClientUids.add(clientUid); - mApp.getWindowProcessController().setBoundClientUids(mBoundClientUids); + mApp.getWindowProcessController() + .addBoundClientUid(clientUid, clientPackageName, bindFlags); } void updateBoundClientUids() { + clearBoundClientUids(); if (mServices.isEmpty()) { - clearBoundClientUids(); return; } // grab a set of clientUids of all mConnections of all services final ArraySet<Integer> boundClientUids = new ArraySet<>(); final int serviceCount = mServices.size(); + WindowProcessController controller = mApp.getWindowProcessController(); for (int j = 0; j < serviceCount; j++) { final ArrayMap<IBinder, ArrayList<ConnectionRecord>> conns = mServices.valueAt(j).getConnections(); @@ -434,12 +437,13 @@ final class ProcessServiceRecord { for (int conni = 0; conni < size; conni++) { ArrayList<ConnectionRecord> c = conns.valueAt(conni); for (int i = 0; i < c.size(); i++) { - boundClientUids.add(c.get(i).clientUid); + ConnectionRecord cr = c.get(i); + boundClientUids.add(cr.clientUid); + controller.addBoundClientUid(cr.clientUid, cr.clientPackageName, cr.flags); } } } mBoundClientUids = boundClientUids; - mApp.getWindowProcessController().setBoundClientUids(mBoundClientUids); } void addBoundClientUidsOfNewService(ServiceRecord sr) { @@ -450,15 +454,18 @@ final class ProcessServiceRecord { for (int conni = conns.size() - 1; conni >= 0; conni--) { ArrayList<ConnectionRecord> c = conns.valueAt(conni); for (int i = 0; i < c.size(); i++) { - mBoundClientUids.add(c.get(i).clientUid); + ConnectionRecord cr = c.get(i); + mBoundClientUids.add(cr.clientUid); + mApp.getWindowProcessController() + .addBoundClientUid(cr.clientUid, cr.clientPackageName, cr.flags); + } } - mApp.getWindowProcessController().setBoundClientUids(mBoundClientUids); } void clearBoundClientUids() { mBoundClientUids.clear(); - mApp.getWindowProcessController().setBoundClientUids(mBoundClientUids); + mApp.getWindowProcessController().clearBoundClientUids(); } @GuardedBy("mService") diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 4b82ad863c8e..0a79b1547558 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -738,7 +738,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN // if we have a process attached, add bound client uid of this connection to it if (app != null) { - app.mServices.addBoundClientUid(c.clientUid); + app.mServices.addBoundClientUid(c.clientUid, c.clientPackageName, c.flags); app.mProfile.addHostingComponentType(HOSTING_COMPONENT_TYPE_BOUND_SERVICE); } } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index f25929c36060..a3eba7da8a00 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -3154,7 +3154,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (!mContext.bindServiceAsUser(intent, newConn, Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE - | Context.BIND_INCLUDE_CAPABILITIES, + | Context.BIND_INCLUDE_CAPABILITIES + | Context.BIND_DENY_ACTIVITY_STARTS_PRE_34, new UserHandle(serviceUserId))) { String msg = "Unable to bind service: " + componentName; diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java index 0afd87282783..0af8dfef7709 100644 --- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java +++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java @@ -25,11 +25,11 @@ import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONL import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; import android.os.Binder; import android.os.IBinder; import android.os.SystemClock; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.IntArray; import android.util.Slog; @@ -61,9 +61,11 @@ class BackgroundLaunchProcessController { @GuardedBy("this") private @Nullable ArrayMap<Binder, IBinder> mBackgroundActivityStartTokens; - /** Set of UIDs of clients currently bound to this process. */ + /** Set of UIDs of clients currently bound to this process and opt in to allow this process to + * launch background activity. + */ @GuardedBy("this") - private @Nullable IntArray mBoundClientUids; + private @Nullable IntArray mBalOptInBoundClientUids; BackgroundLaunchProcessController(@NonNull IntPredicate uidHasActiveVisibleWindowPredicate, @Nullable BackgroundActivityStartCallback callback) { @@ -169,9 +171,9 @@ class BackgroundLaunchProcessController { private boolean isBoundByForegroundUid() { synchronized (this) { - if (mBoundClientUids != null) { - for (int i = mBoundClientUids.size() - 1; i >= 0; i--) { - if (mUidHasActiveVisibleWindowPredicate.test(mBoundClientUids.get(i))) { + if (mBalOptInBoundClientUids != null) { + for (int i = mBalOptInBoundClientUids.size() - 1; i >= 0; i--) { + if (mUidHasActiveVisibleWindowPredicate.test(mBalOptInBoundClientUids.get(i))) { return true; } } @@ -180,19 +182,23 @@ class BackgroundLaunchProcessController { return false; } - void setBoundClientUids(ArraySet<Integer> boundClientUids) { + void clearBalOptInBoundClientUids() { synchronized (this) { - if (boundClientUids == null || boundClientUids.isEmpty()) { - mBoundClientUids = null; - return; - } - if (mBoundClientUids == null) { - mBoundClientUids = new IntArray(); + if (mBalOptInBoundClientUids == null) { + mBalOptInBoundClientUids = new IntArray(); } else { - mBoundClientUids.clear(); + mBalOptInBoundClientUids.clear(); + } + } + } + + void addBoundClientUid(int clientUid, String clientPackageName, int bindFlags) { + if ((bindFlags & Context.BIND_DENY_ACTIVITY_STARTS_PRE_34) == 0) { + if (mBalOptInBoundClientUids == null) { + mBalOptInBoundClientUids = new IntArray(); } - for (int i = boundClientUids.size() - 1; i >= 0; i--) { - mBoundClientUids.add(boundClientUids.valueAt(i)); + if (mBalOptInBoundClientUids.indexOf(clientUid) == -1) { + mBalOptInBoundClientUids.add(clientUid); } } } @@ -258,10 +264,10 @@ class BackgroundLaunchProcessController { pw.println(mBackgroundActivityStartTokens.valueAt(i)); } } - if (mBoundClientUids != null && mBoundClientUids.size() > 0) { + if (mBalOptInBoundClientUids != null && mBalOptInBoundClientUids.size() > 0) { pw.print(prefix); pw.print("BoundClientUids:"); - pw.println(Arrays.toString(mBoundClientUids.toArray())); + pw.println(Arrays.toString(mBalOptInBoundClientUids.toArray())); } } } diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 40417a4857d3..22280cdb71fb 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -538,8 +538,18 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return mBgLaunchController.canCloseSystemDialogsByToken(mUid); } - public void setBoundClientUids(ArraySet<Integer> boundClientUids) { - mBgLaunchController.setBoundClientUids(boundClientUids); + /** + * Clear all bound client Uids. + */ + public void clearBoundClientUids() { + mBgLaunchController.clearBalOptInBoundClientUids(); + } + + /** + * Add bound client Uid. + */ + public void addBoundClientUid(int clientUid, String clientPackageName, int bindFlags) { + mBgLaunchController.addBoundClientUid(clientUid, clientPackageName, bindFlags); } /** |