diff options
101 files changed, 1969 insertions, 757 deletions
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java index 850a1d25339c..381efc10b416 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java @@ -227,7 +227,8 @@ public class BlobStoreManagerService extends SystemService { int n = 0; long sessionId; do { - sessionId = Math.abs(mRandom.nextLong()); + final long randomLong = mRandom.nextLong(); + sessionId = (randomLong == Long.MIN_VALUE) ? INVALID_BLOB_ID : Math.abs(randomLong); if (mKnownBlobIds.indexOf(sessionId) < 0 && sessionId != INVALID_BLOB_ID) { return sessionId; } @@ -647,6 +648,17 @@ public class BlobStoreManagerService extends SystemService { session.getOwnerUid(), blob.getBlobId(), blob.getSize(), FrameworkStatsLog.BLOB_COMMITTED__RESULT__ERROR_DURING_COMMIT); session.sendCommitCallbackResult(COMMIT_RESULT_ERROR); + // If the commit fails and this blob data didn't exist before, delete it. + // But if it is a recommit, just leave it as is. + if (session.getSessionId() == blob.getBlobId()) { + deleteBlobLocked(blob); + userBlobs.remove(blob.getBlobHandle()); + } + } + // Delete redundant data from recommits. + if (session.getSessionId() != blob.getBlobId()) { + session.getSessionFile().delete(); + mActiveBlobIds.remove(session.getSessionId()); } getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid())) .remove(session.getSessionId()); @@ -1543,7 +1555,7 @@ public class BlobStoreManagerService extends SystemService { public int handleShellCommand(@NonNull ParcelFileDescriptor in, @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, @NonNull String[] args) { - return (new BlobStoreManagerShellCommand(BlobStoreManagerService.this)).exec(this, + return new BlobStoreManagerShellCommand(BlobStoreManagerService.this).exec(this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args); } } diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java index 51cf805aa000..77ca4aa7a9e6 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java @@ -100,7 +100,7 @@ class BlobStoreSession extends IBlobStoreSession.Stub { private File mSessionFile; @GuardedBy("mRevocableFds") - private ArrayList<RevocableFileDescriptor> mRevocableFds = new ArrayList<>(); + private final ArrayList<RevocableFileDescriptor> mRevocableFds = new ArrayList<>(); // This will be accessed from only one thread at any point of time, so no need to grab // a lock for this. diff --git a/api/system-current.txt b/api/system-current.txt index d95eaded32b6..090f8fe58c35 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -7059,6 +7059,7 @@ package android.net.wifi { method public int getBand(); method @NonNull public java.util.List<android.net.MacAddress> getBlockedClientList(); method public int getChannel(); + method public int getMacRandomizationSetting(); method public int getMaxNumberOfClients(); method public long getShutdownTimeoutMillis(); method public boolean isAutoShutdownEnabled(); @@ -7068,6 +7069,8 @@ package android.net.wifi { field public static final int BAND_5GHZ = 2; // 0x2 field public static final int BAND_6GHZ = 4; // 0x4 field public static final int BAND_ANY = 7; // 0x7 + field public static final int RANDOMIZATION_NONE = 0; // 0x0 + field public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1 } public static final class SoftApConfiguration.Builder { @@ -7082,6 +7085,7 @@ package android.net.wifi { method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int, int); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setClientControlByUserEnabled(boolean); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMacRandomizationSetting(int); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(@IntRange(from=0) int); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setPassphrase(@Nullable String, int); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(@IntRange(from=0) long); @@ -7091,6 +7095,7 @@ package android.net.wifi { public final class SoftApInfo implements android.os.Parcelable { method public int describeContents(); method public int getBandwidth(); + method @Nullable public android.net.MacAddress getBssid(); method public int getFrequency(); method public void writeToParcel(@NonNull android.os.Parcel, int); field public static final int CHANNEL_WIDTH_160MHZ = 6; // 0x6 diff --git a/api/test-current.txt b/api/test-current.txt index e788e6f6d8e2..dbcedbaf2b45 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -5098,6 +5098,7 @@ package android.view { method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut(); method public int getType(); method public boolean hasAccess(int); + field public static final int FLAG_TRUSTED = 128; // 0x80 field public static final int TYPE_EXTERNAL = 2; // 0x2 field public static final int TYPE_INTERNAL = 1; // 0x1 field public static final int TYPE_OVERLAY = 4; // 0x4 diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index c6959a03ec72..89e1f5a28a35 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -6520,7 +6520,8 @@ public final class ActivityThread extends ClientTransactionHandler { } // Allow binder tracing, and application-generated systrace messages if we're profileable. - boolean isAppProfileable = data.appInfo.isProfileableByShell(); + boolean isAppDebuggable = (data.appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; + boolean isAppProfileable = isAppDebuggable || data.appInfo.isProfileableByShell(); Trace.setAppTracingAllowed(isAppProfileable); if ((isAppProfileable || Build.IS_DEBUGGABLE) && data.enableBinderTracking) { Binder.enableTracing(); @@ -6532,7 +6533,6 @@ public final class ActivityThread extends ClientTransactionHandler { } // Allow renderer debugging features if we're debuggable. - boolean isAppDebuggable = (data.appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; HardwareRenderer.setDebuggingEnabled(isAppDebuggable || Build.IS_DEBUGGABLE); HardwareRenderer.setPackageName(data.appInfo.packageName); diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index d650bbcdfa33..b749c3504811 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -360,18 +360,9 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd } /** - * Release this container. Activity launching will no longer be permitted. - * <p>Note: Calling this method is allowed after - * {@link StateCallback#onActivityViewReady(ActivityView)} callback was triggered and before - * {@link StateCallback#onActivityViewDestroyed(ActivityView)}. - * - * @see StateCallback + * Release this container if it is initialized. Activity launching will no longer be permitted. */ public void release() { - if (!mTaskEmbedder.isInitialized()) { - throw new IllegalStateException( - "Trying to release container that is not initialized."); - } performRelease(); } @@ -487,7 +478,9 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd return; } mSurfaceView.getHolder().removeCallback(mSurfaceCallback); - mTaskEmbedder.release(); + if (mTaskEmbedder.isInitialized()) { + mTaskEmbedder.release(); + } mTaskEmbedder.setListener(null); mGuard.close(); diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl index 254657e26f3a..37c5cabc2376 100644 --- a/core/java/android/app/IBackupAgent.aidl +++ b/core/java/android/app/IBackupAgent.aidl @@ -42,11 +42,7 @@ oneway interface IBackupAgent { * @param newState Read-write file, empty when onBackup() is called, * where the new state blob is to be recorded. * - * @param quota Quota reported by the transport for this backup operation (in bytes). - * - * @param token Opaque token identifying this transaction. This must - * be echoed back to the backup service binder once the new - * data has been written to the data and newState files. + * @param quotaBytes Quota reported by the transport for this backup operation (in bytes). * * @param callbackBinder Binder on which to indicate operation completion. * @@ -106,7 +102,7 @@ oneway interface IBackupAgent { * The data must be formatted correctly for the resulting archive to be * legitimate, so that will be tightly controlled by the available API. * - * @param quota Quota reported by the transport for this backup operation (in bytes). + * @param quotaBytes Quota reported by the transport for this backup operation (in bytes). * * @param token Opaque token identifying this transaction. This must * be echoed back to the backup service binder once the agent is diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 7ac1f5c0e344..d910974d2484 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1648,10 +1648,6 @@ public class PackageParser { final String attr = attrs.getAttributeName(i); if ("debuggable".equals(attr)) { debuggable = attrs.getAttributeBooleanValue(i, false); - if (debuggable) { - // Debuggable implies profileable - profilableByShell = true; - } } if ("multiArch".equals(attr)) { multiArch = attrs.getAttributeBooleanValue(i, false); @@ -3471,8 +3467,6 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestApplication_debuggable, false)) { ai.flags |= ApplicationInfo.FLAG_DEBUGGABLE; - // Debuggable implies profileable - ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL; } if (sa.getBoolean( diff --git a/core/java/android/content/pm/dex/ArtManagerInternal.java b/core/java/android/content/pm/dex/ArtManagerInternal.java index 62ab9e02f858..23fef29803e7 100644 --- a/core/java/android/content/pm/dex/ArtManagerInternal.java +++ b/core/java/android/content/pm/dex/ArtManagerInternal.java @@ -30,5 +30,5 @@ public abstract class ArtManagerInternal { * in executes using the specified {@code abi}. */ public abstract PackageOptimizationInfo getPackageOptimizationInfo( - ApplicationInfo info, String abi); + ApplicationInfo info, String abi, String activityName); } diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java index 9c03fa6e04b2..4800f8954575 100644 --- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java @@ -380,10 +380,6 @@ public class ApkLiteParseUtils { switch (attr) { case "debuggable": debuggable = attrs.getAttributeBooleanValue(i, false); - if (debuggable) { - // Debuggable implies profileable - profilableByShell = true; - } break; case "multiArch": multiArch = attrs.getAttributeBooleanValue(i, false); diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index 317107829623..1a0c27e50a78 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -1720,8 +1720,7 @@ public class ParsingPackageUtils { // TODO(b/135203078): Should parsing code be responsible for this? Maybe move to a // util or just have PackageImpl return true if either flag is set - // Debuggable implies profileable - pkg.setProfileableByShell(pkg.isProfileableByShell() || pkg.isDebuggable()); + pkg.setProfileableByShell(pkg.isProfileableByShell()); if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) { pkg.setResizeableActivity(sa.getBoolean( diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index ea5cc7f2e8bc..c1ba2094d3cf 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -303,13 +303,25 @@ public final class DisplayManager { /** * Virtual display flag: Indicates that the display should support system decorations. Virtual * displays without this flag shouldn't show home, IME or any other system decorations. + * <p>This flag doesn't work without {@link #VIRTUAL_DISPLAY_FLAG_TRUSTED}</p> * * @see #createVirtualDisplay + * @see #VIRTUAL_DISPLAY_FLAG_TRUSTED * @hide */ // TODO (b/114338689): Remove the flag and use IWindowManager#setShouldShowSystemDecors public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 9; + /** + * Virtual display flags: Indicates that the display is trusted to show system decorations and + * receive inputs without users' touch. + * + * @see #createVirtualDisplay + * @see #VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS + * @hide + */ + public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1 << 10; + /** @hide */ public DisplayManager(Context context) { mContext = context; diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index e8806a03d00e..0abf8ae352af 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -16,9 +16,11 @@ package android.os.storage; +import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; import static android.app.AppOpsManager.OP_LEGACY_STORAGE; +import static android.app.AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE; import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE; import static android.app.AppOpsManager.OP_READ_MEDIA_AUDIO; import static android.app.AppOpsManager.OP_READ_MEDIA_IMAGES; @@ -1853,7 +1855,7 @@ public class StorageManager { /** {@hide} */ public boolean checkPermissionReadAudio(boolean enforce, int pid, int uid, String packageName, @Nullable String featureId) { - if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId, + if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId, READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) { return false; } @@ -1864,7 +1866,7 @@ public class StorageManager { /** {@hide} */ public boolean checkPermissionWriteAudio(boolean enforce, int pid, int uid, String packageName, @Nullable String featureId) { - if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId, + if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId, WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) { return false; } @@ -1875,7 +1877,7 @@ public class StorageManager { /** {@hide} */ public boolean checkPermissionReadVideo(boolean enforce, int pid, int uid, String packageName, @Nullable String featureId) { - if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId, + if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId, READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) { return false; } @@ -1886,7 +1888,7 @@ public class StorageManager { /** {@hide} */ public boolean checkPermissionWriteVideo(boolean enforce, int pid, int uid, String packageName, @Nullable String featureId) { - if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId, + if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId, WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) { return false; } @@ -1897,7 +1899,7 @@ public class StorageManager { /** {@hide} */ public boolean checkPermissionReadImages(boolean enforce, int pid, int uid, String packageName, @Nullable String featureId) { - if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId, + if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId, READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) { return false; } @@ -1908,7 +1910,7 @@ public class StorageManager { /** {@hide} */ public boolean checkPermissionWriteImages(boolean enforce, int pid, int uid, String packageName, @Nullable String featureId) { - if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId, + if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId, WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) { return false; } @@ -1916,6 +1918,24 @@ public class StorageManager { OP_WRITE_MEDIA_IMAGES); } + private boolean checkExternalStoragePermissionAndAppOp(boolean enforce, + int pid, int uid, String packageName, @Nullable String featureId, String permission, + int op) { + // First check if app has MANAGE_EXTERNAL_STORAGE. + final int mode = mAppOps.noteOpNoThrow(OP_MANAGE_EXTERNAL_STORAGE, uid, packageName, + featureId, null); + if (mode == AppOpsManager.MODE_ALLOWED) { + return true; + } + if (mode == AppOpsManager.MODE_DEFAULT && mContext.checkPermission( + MANAGE_EXTERNAL_STORAGE, pid, uid) == PERMISSION_GRANTED) { + return true; + } + // If app doesn't have MANAGE_EXTERNAL_STORAGE, then check if it has requested granular + // permission. + return checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId, permission, op); + } + /** {@hide} */ @VisibleForTesting public @NonNull ParcelFileDescriptor openProxyFileDescriptor( diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index 657622ca9a36..a6e6d057d48c 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -219,6 +219,9 @@ public class PhoneStateListener { /** * Listen for changes to observed cell info. * + * Listening to this event requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} + * permission. + * * @see #onCellInfoChanged */ public static final int LISTEN_CELL_INFO = 0x00000400; @@ -461,6 +464,9 @@ public class PhoneStateListener { * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). * + * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless + * of whether the calling app has carrier privileges. + * * @see #onRegistrationFailed */ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) @@ -472,6 +478,9 @@ public class PhoneStateListener { * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). * + * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless + * of whether the calling app has carrier privileges. + * * @see #onBarringInfoChanged */ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) @@ -569,6 +578,11 @@ public class PhoneStateListener { * subId. Otherwise, this callback applies to * {@link SubscriptionManager#getDefaultSubscriptionId()}. * + * The instance of {@link ServiceState} passed as an argument here will have various levels of + * location information stripped from it depending on the location permissions that your app + * holds. Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will + * receive all the information in {@link ServiceState}. + * * @see ServiceState#STATE_EMERGENCY_ONLY * @see ServiceState#STATE_IN_SERVICE * @see ServiceState#STATE_OUT_OF_SERVICE diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 8db1703a627f..0cc469a2d5eb 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -241,13 +241,26 @@ public final class Display { * This flag identifies secondary displays that should show system decorations, such as status * bar, navigation bar, home activity or IME. * </p> + * <p>Note that this flag doesn't work without {@link #FLAG_TRUSTED}</p> * + * @see #getFlags() * @hide */ // TODO (b/114338689): Remove the flag and use IWindowManager#setShouldShowSystemDecors public static final int FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 6; /** + * Flag: The display is trusted to show system decorations and receive inputs without users' + * touch. + * @see #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS + * + * @see #getFlags() + * @hide + */ + @TestApi + public static final int FLAG_TRUSTED = 1 << 7; + + /** * Display flag: Indicates that the contents of the display should not be scaled * to fit the physical screen dimensions. Used for development only to emulate * devices with smaller physicals screens while preserving density. @@ -564,6 +577,7 @@ public final class Display { * @see #FLAG_SUPPORTS_PROTECTED_BUFFERS * @see #FLAG_SECURE * @see #FLAG_PRIVATE + * @see #FLAG_ROUND */ public int getFlags() { return mFlags; @@ -1222,6 +1236,16 @@ public final class Display { Display.FLAG_PRESENTATION; } + /** + * @return {@code true} if the display is a trusted display. + * + * @see #FLAG_TRUSTED + * @hide + */ + public boolean isTrusted() { + return (mFlags & FLAG_TRUSTED) == FLAG_TRUSTED; + } + private void updateDisplayInfoLocked() { // Note: The display manager caches display info objects on our behalf. DisplayInfo newInfo = mGlobal.getDisplayInfo(mDisplayId); diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index d369883f3ac3..b1ede4102bec 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -717,6 +717,15 @@ public final class DisplayInfo implements Parcelable { if ((flags & Display.FLAG_ROUND) != 0) { result.append(", FLAG_ROUND"); } + if ((flags & Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) { + result.append(", FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD"); + } + if ((flags & Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) { + result.append(", FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS"); + } + if ((flags & Display.FLAG_TRUSTED) != 0) { + result.append(", FLAG_TRUSTED"); + } return result.toString(); } } diff --git a/core/java/android/view/EventLogTags.logtags b/core/java/android/view/EventLogTags.logtags new file mode 100644 index 000000000000..098f1af582c3 --- /dev/null +++ b/core/java/android/view/EventLogTags.logtags @@ -0,0 +1,49 @@ +# See system/core/logcat/event.logtags for a description of the format of this file. + +option java_package android.view + +# The entries in this file map a sparse set of log tag numbers to tag names. +# This is installed on the device, in /system/etc, and parsed by logcat. +# +# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the +# negative values alone for now.) +# +# Tag names are one or more ASCII letters and numbers or underscores, i.e. +# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former +# impacts log readability, the latter makes regex searches more annoying). +# +# Tag numbers and names are separated by whitespace. Blank lines and lines +# starting with '#' are ignored. +# +# Optionally, after the tag names can be put a description for the value(s) +# of the tag. Description are in the format +# (<name>|data type[|data unit]) +# Multiple values are separated by commas. +# +# The data type is a number from the following values: +# 1: int +# 2: long +# 3: string +# 4: list +# +# The data unit is a number taken from the following list: +# 1: Number of objects +# 2: Number of bytes +# 3: Number of milliseconds +# 4: Number of allocations +# 5: Id +# 6: Percent +# Default value for data of type int/long is 2 (bytes). +# +# See system/core/logcat/event.logtags for the master copy of the tags. + +# 62000 - 62199 reserved for inputflinger + +# --------------------------- +# android.view +# --------------------------- +# Enqueue Input Event +62002 view_enqueue_input_event (eventType|3),(action|3) + +# NOTE - the range 1000000-2000000 is reserved for partners and others who +# want to define their own log tags without conflicting with the core platform.
\ No newline at end of file diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java index 713cfb48c95f..064bc6947fc4 100644 --- a/core/java/android/view/FocusFinder.java +++ b/core/java/android/view/FocusFinder.java @@ -311,6 +311,9 @@ public class FocusFinder { } final int count = focusables.size(); + if (count < 2) { + return null; + } switch (direction) { case View.FOCUS_FORWARD: return getNextFocusable(focused, focusables, count); @@ -373,29 +376,29 @@ public class FocusFinder { } private static View getNextFocusable(View focused, ArrayList<View> focusables, int count) { + if (count < 2) { + return null; + } if (focused != null) { int position = focusables.lastIndexOf(focused); if (position >= 0 && position + 1 < count) { return focusables.get(position + 1); } } - if (!focusables.isEmpty()) { - return focusables.get(0); - } - return null; + return focusables.get(0); } private static View getPreviousFocusable(View focused, ArrayList<View> focusables, int count) { + if (count < 2) { + return null; + } if (focused != null) { int position = focusables.indexOf(focused); if (position > 0) { return focusables.get(position - 1); } } - if (!focusables.isEmpty()) { - return focusables.get(count - 1); - } - return null; + return focusables.get(count - 1); } private static View getNextKeyboardNavigationCluster( diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 9a9396c45b66..de786bed3144 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -260,6 +260,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private final boolean mHasAnimationCallbacks; private final @InsetsType int mRequestedTypes; private final long mDurationMs; + private final boolean mDisable; private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal = new ThreadLocal<AnimationHandler>() { @@ -272,11 +273,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation }; public InternalAnimationControlListener(boolean show, boolean hasAnimationCallbacks, - int requestedTypes) { + int requestedTypes, boolean disable) { mShow = show; mHasAnimationCallbacks = hasAnimationCallbacks; mRequestedTypes = requestedTypes; mDurationMs = calculateDurationMs(); + mDisable = disable; } @Override @@ -284,6 +286,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation mController = controller; if (DEBUG) Log.d(TAG, "default animation onReady types: " + types); + if (mDisable) { + onAnimationFinish(); + return; + } mAnimator = ValueAnimator.ofFloat(0f, 1f); mAnimator.setDuration(mDurationMs); mAnimator.setInterpolator(new LinearInterpolator()); @@ -477,6 +483,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private DisplayCutout mLastDisplayCutout; private boolean mStartingAnimation; private int mCaptionInsetsHeight = 0; + private boolean mAnimationsDisabled; private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest; private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners @@ -485,6 +492,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation /** Set of inset types for which an animation was started since last resetting this field */ private @InsetsType int mLastStartedAnimTypes; + /** Set of inset types which cannot be controlled by the user animation */ + private @InsetsType int mLastDisabledUserAnimationInsetsTypes; + + private Runnable mInvokeControllableInsetsChangedListeners = + this::invokeControllableInsetsChangedListeners; + public InsetsController(Host host) { this(host, (controller, type) -> { if (type == ITYPE_IME) { @@ -599,9 +612,23 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private void updateState(InsetsState newState) { mState.setDisplayFrame(newState.getDisplayFrame()); + @InsetsType int disabledUserAnimationTypes = 0; + @InsetsType int[] cancelledUserAnimationTypes = {0}; for (int i = newState.getSourcesCount() - 1; i >= 0; i--) { InsetsSource source = newState.sourceAt(i); - getSourceConsumer(source.getType()).updateSource(source); + @InternalInsetsType int internalInsetsType = source.getType(); + @AnimationType int animationType = getAnimationType(internalInsetsType); + if (source.isVisibleFrameEmpty()) { + @InsetsType int insetsType = toPublicType(internalInsetsType); + // The user animation is not allowed when visible frame is empty. + disabledUserAnimationTypes |= insetsType; + if (animationType == ANIMATION_TYPE_USER) { + // Existing user animation needs to be cancelled. + animationType = ANIMATION_TYPE_NONE; + cancelledUserAnimationTypes[0] |= insetsType; + } + } + getSourceConsumer(internalInsetsType).updateSource(source, animationType); } for (int i = mState.getSourcesCount() - 1; i >= 0; i--) { InsetsSource source = mState.sourceAt(i); @@ -613,6 +640,27 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top, mFrame.right, mFrame.top + mCaptionInsetsHeight)); } + + updateDisabledUserAnimationTypes(disabledUserAnimationTypes); + + if (cancelledUserAnimationTypes[0] != 0) { + mHandler.post(() -> show(cancelledUserAnimationTypes[0])); + } + } + + private void updateDisabledUserAnimationTypes(@InsetsType int disabledUserAnimationTypes) { + @InsetsType int diff = mLastDisabledUserAnimationInsetsTypes ^ disabledUserAnimationTypes; + if (diff != 0) { + for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { + InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); + if (consumer.getControl() != null && (toPublicType(consumer.mType) & diff) != 0) { + mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners); + mHandler.post(mInvokeControllableInsetsChangedListeners); + break; + } + } + mLastDisabledUserAnimationInsetsTypes = disabledUserAnimationTypes; + } } private boolean captionInsetsUnchanged() { @@ -896,6 +944,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation boolean imeReady = true; for (int i = internalTypes.size() - 1; i >= 0; i--) { final InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); + if (animationType == ANIMATION_TYPE_USER) { + final InsetsSource source = mState.peekSource(consumer.getType()); + if (source != null && source.isVisibleFrameEmpty()) { + if (WARN) Log.w(TAG, String.format( + "collectSourceControls can't run user animation for type: %s", + InsetsState.typeToString(consumer.getType()))); + continue; + } + } boolean show = animationType == ANIMATION_TYPE_SHOW || animationType == ANIMATION_TYPE_USER; boolean canRun = false; @@ -1163,8 +1220,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } boolean hasAnimationCallbacks = mHost.hasAnimationCallbacks(); - final InternalAnimationControlListener listener = - new InternalAnimationControlListener(show, hasAnimationCallbacks, types); + final InternalAnimationControlListener listener = new InternalAnimationControlListener( + show, hasAnimationCallbacks, types, mAnimationsDisabled); // Show/hide animations always need to be relative to the display frame, in order that shown // and hidden state insets are correct. @@ -1279,11 +1336,17 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return mHost.getSystemBarsBehavior(); } + @Override + public void setAnimationsDisabled(boolean disable) { + mAnimationsDisabled = disable; + } + private @InsetsType int calculateControllableTypes() { @InsetsType int result = 0; for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); - if (consumer.getControl() != null) { + InsetsSource source = mState.peekSource(consumer.mType); + if (consumer.getControl() != null && source != null && !source.isVisibleFrameEmpty()) { result |= toPublicType(consumer.mType); } } @@ -1294,6 +1357,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation * @return The types that are now animating due to a listener invoking control/show/hide */ private @InsetsType int invokeControllableInsetsChangedListeners() { + mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners); mLastStartedAnimTypes = 0; @InsetsType int types = calculateControllableTypes(); int size = mControllableInsetsChangedListeners.size(); diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index 15b9a9330392..c8394d25ef47 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -92,6 +92,10 @@ public class InsetsSource implements Parcelable { return mVisible; } + public boolean isVisibleFrameEmpty() { + return mVisibleFrame != null && mVisibleFrame.isEmpty(); + } + /** * Calculates the insets this source will cause to a client window. * diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index 3aa246441dbc..8d92d7b5ab20 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -275,9 +275,9 @@ public class InsetsSourceConsumer { } @VisibleForTesting(visibility = PACKAGE) - public void updateSource(InsetsSource newSource) { + public void updateSource(InsetsSource newSource, @AnimationType int animationType) { InsetsSource source = mState.peekSource(mType); - if (source == null || mController.getAnimationType(mType) == ANIMATION_TYPE_NONE + if (source == null || animationType == ANIMATION_TYPE_NONE || source.getFrame().equals(newSource.getFrame())) { mPendingFrame = null; mPendingVisibleFrame = null; @@ -286,7 +286,7 @@ public class InsetsSourceConsumer { } // Frame is changing while animating. Keep note of the new frame but keep existing frame - // until animaition is finished. + // until animation is finished. newSource = new InsetsSource(newSource); mPendingFrame = new Rect(newSource.getFrame()); mPendingVisibleFrame = newSource.getVisibleFrame() != null diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java index 0283ada0dd40..c018d1cf1782 100644 --- a/core/java/android/view/PendingInsetsController.java +++ b/core/java/android/view/PendingInsetsController.java @@ -38,6 +38,7 @@ public class PendingInsetsController implements WindowInsetsController { private @Appearance int mAppearance; private @Appearance int mAppearanceMask; private @Behavior int mBehavior = KEEP_BEHAVIOR; + private boolean mAnimationsDisabled; private final InsetsState mDummyState = new InsetsState(); private InsetsController mReplayedInsetsController; private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners @@ -103,6 +104,15 @@ public class PendingInsetsController implements WindowInsetsController { } @Override + public void setAnimationsDisabled(boolean disable) { + if (mReplayedInsetsController != null) { + mReplayedInsetsController.setAnimationsDisabled(disable); + } else { + mAnimationsDisabled = disable; + } + } + + @Override public InsetsState getState() { return mDummyState; } @@ -151,6 +161,9 @@ public class PendingInsetsController implements WindowInsetsController { if (mCaptionInsetsHeight != 0) { controller.setCaptionInsetsHeight(mCaptionInsetsHeight); } + if (mAnimationsDisabled) { + controller.setAnimationsDisabled(true); + } int size = mRequests.size(); for (int i = 0; i < size; i++) { mRequests.get(i).replay(controller); @@ -167,6 +180,7 @@ public class PendingInsetsController implements WindowInsetsController { mBehavior = KEEP_BEHAVIOR; mAppearance = 0; mAppearanceMask = 0; + mAnimationsDisabled = false; // After replaying, we forward everything directly to the replayed instance. mReplayedInsetsController = controller; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 8b5d033072fb..bb3269ff56ac 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -115,6 +115,7 @@ import android.os.UserHandle; import android.sysprop.DisplayProperties; import android.util.AndroidRuntimeException; import android.util.DisplayMetrics; +import android.util.EventLog; import android.util.Log; import android.util.LongArray; import android.util.MergedConfiguration; @@ -7946,6 +7947,19 @@ public final class ViewRootImpl implements ViewParent, InputEventReceiver receiver, int flags, boolean processImmediately) { QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); + if (event instanceof MotionEvent) { + MotionEvent me = (MotionEvent) event; + if (me.getAction() == MotionEvent.ACTION_CANCEL) { + EventLog.writeEvent(EventLogTags.VIEW_ENQUEUE_INPUT_EVENT, "Motion - Cancel", + getTitle()); + } + } else if (event instanceof KeyEvent) { + KeyEvent ke = (KeyEvent) event; + if (ke.isCanceled()) { + EventLog.writeEvent(EventLogTags.VIEW_ENQUEUE_INPUT_EVENT, "Key - Cancel", + getTitle()); + } + } // Always enqueue the input event in order, regardless of its time stamp. // We do this because the application or the IME may inject key events // in response to touch events and we want to ensure that the injected keys diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java index 3d348efc7f0f..1a9003581078 100644 --- a/core/java/android/view/WindowInsetsController.java +++ b/core/java/android/view/WindowInsetsController.java @@ -222,6 +222,13 @@ public interface WindowInsetsController { @Behavior int getSystemBarsBehavior(); /** + * Disables or enables the animations. + * + * @hide + */ + void setAnimationsDisabled(boolean disable); + + /** * @hide */ InsetsState getState(); diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java index 7455ad009873..11e55b852516 100644 --- a/core/java/com/android/internal/app/procstats/ProcessStats.java +++ b/core/java/com/android/internal/app/procstats/ProcessStats.java @@ -2232,24 +2232,43 @@ public final class ProcessStats implements Parcelable { } /** Similar to {@code #dumpDebug}, but with a reduced/aggregated subset of states. */ - public void dumpAggregatedProtoForStatsd(ProtoOutputStream proto) { - dumpProtoPreamble(proto); + public void dumpAggregatedProtoForStatsd(ProtoOutputStream[] protoStreams, + long maxRawShardSizeBytes) { + int shardIndex = 0; + dumpProtoPreamble(protoStreams[shardIndex]); + final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); final ProcessMap<ArraySet<PackageState>> procToPkgMap = new ProcessMap<>(); final SparseArray<ArraySet<String>> uidToPkgMap = new SparseArray<>(); collectProcessPackageMaps(null, false, procToPkgMap, uidToPkgMap); + for (int ip = 0; ip < procMap.size(); ip++) { final String procName = procMap.keyAt(ip); + if (protoStreams[shardIndex].getRawSize() > maxRawShardSizeBytes) { + shardIndex++; + if (shardIndex >= protoStreams.length) { + // We have run out of space; we'll drop the rest of the processes. + Slog.d(TAG, String.format("Dropping process indices from %d to %d from " + + "statsd proto (too large)", ip, procMap.size())); + break; + } + dumpProtoPreamble(protoStreams[shardIndex]); + } + final SparseArray<ProcessState> uids = procMap.valueAt(ip); for (int iu = 0; iu < uids.size(); iu++) { final int uid = uids.keyAt(iu); final ProcessState procState = uids.valueAt(iu); - procState.dumpAggregatedProtoForStatsd(proto, + procState.dumpAggregatedProtoForStatsd(protoStreams[shardIndex], ProcessStatsSectionProto.PROCESS_STATS, procName, uid, mTimePeriodEndRealtime, procToPkgMap, uidToPkgMap); } } + + for (int i = 0; i <= shardIndex; i++) { + protoStreams[i].flush(); + } } private void dumpProtoPreamble(ProtoOutputStream proto) { @@ -2403,10 +2422,11 @@ public final class ProcessStats implements Parcelable { final SourceKey key = assocVals.keyAt(i); final long[] vals = assocVals.valueAt(i); final long token = proto.start(fieldId); + final int idx = uidToPkgMap.indexOfKey(key.mUid); ProcessState.writeCompressedProcessName(proto, ProcessStatsAssociationProto.ASSOC_PROCESS_NAME, key.mProcess, key.mPackage, - uidToPkgMap.get(key.mUid).size() > 1); + idx >= 0 && uidToPkgMap.valueAt(idx).size() > 1); proto.write(ProcessStatsAssociationProto.ASSOC_UID, key.mUid); proto.write(ProcessStatsAssociationProto.TOTAL_COUNT, (int) vals[1]); proto.write(ProcessStatsAssociationProto.TOTAL_DURATION_SECS, diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 3f03f2a3e754..d22f94213338 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -71,4 +71,6 @@ interface IInputMethodManager { void reportActivityView(in IInputMethodClient parentClient, int childDisplayId, in float[] matrixValues); + + void removeImeSurface(); } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 93ec8cbc950f..d24b5e0c5ef9 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5027,6 +5027,10 @@ <permission android:name="android.permission.ACCESS_TV_DESCRAMBLER" android:protectionLevel="signature|privileged|vendorPrivileged" /> + <!-- Allows an application to create trusted displays. @hide --> + <permission android:name="android.permission.ADD_TRUSTED_DISPLAY" + android:protectionLevel="signature" /> + <!-- @hide @SystemApi Allows an application to access locusId events in the usage stats. --> <permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS" android:protectionLevel="signature|appPredictor" /> diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java index bf7f339a8484..1b3272572db0 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java @@ -26,13 +26,11 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertTrue; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; import android.app.Instrumentation; import android.content.Context; @@ -135,37 +133,29 @@ public class InsetsSourceConsumerTest { InsetsSourceConsumer consumer = new InsetsSourceConsumer( ITYPE_IME, state, null, controller); - when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_NONE); - InsetsSource source = new InsetsSource(ITYPE_IME); source.setFrame(0, 1, 2, 3); - consumer.updateSource(new InsetsSource(source)); - - when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_USER); + consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_NONE); // While we're animating, updates are delayed source.setFrame(4, 5, 6, 7); - consumer.updateSource(new InsetsSource(source)); + consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_USER); assertEquals(new Rect(0, 1, 2, 3), state.peekSource(ITYPE_IME).getFrame()); // Finish the animation, now the pending frame should be applied - when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_NONE); assertTrue(consumer.notifyAnimationFinished()); assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame()); - when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_USER); - // Animating again, updates are delayed source.setFrame(8, 9, 10, 11); - consumer.updateSource(new InsetsSource(source)); + consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_USER); assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame()); // Updating with the current frame triggers a different code path, verify this clears // the pending 8, 9, 10, 11 frame: source.setFrame(4, 5, 6, 7); - consumer.updateSource(new InsetsSource(source)); + consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_USER); - when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_NONE); assertFalse(consumer.notifyAnimationFinished()); assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame()); } diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 2bd52d4d8990..dc1612c429e3 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -42,7 +42,7 @@ namespace renderthread { // to the screen resolution. This is meant to be a conservative default based on // that analysis. The 4.0f is used because the default pixel format is assumed to // be ARGB_8888. -#define SURFACE_SIZE_MULTIPLIER (5.0f * 4.0f) +#define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f) #define BACKGROUND_RETENTION_PERCENTAGE (0.5f) CacheManager::CacheManager() diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java index ea8a62f84164..3c647a7ec465 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java @@ -48,7 +48,6 @@ public class PowerWhitelistBackend { private final IDeviceIdleController mDeviceIdleService; private final ArraySet<String> mWhitelistedApps = new ArraySet<>(); private final ArraySet<String> mSysWhitelistedApps = new ArraySet<>(); - private final ArraySet<String> mSysWhitelistedAppsExceptIdle = new ArraySet<>(); private final ArraySet<String> mDefaultActiveApps = new ArraySet<>(); public PowerWhitelistBackend(Context context) { @@ -117,23 +116,6 @@ public class PowerWhitelistBackend { return false; } - public boolean isSysWhitelistedExceptIdle(String pkg) { - return mSysWhitelistedAppsExceptIdle.contains(pkg); - } - - public boolean isSysWhitelistedExceptIdle(String[] pkgs) { - if (ArrayUtils.isEmpty(pkgs)) { - return false; - } - for (String pkg : pkgs) { - if (isSysWhitelistedExceptIdle(pkg)) { - return true; - } - } - - return false; - } - public void addApp(String pkg) { try { mDeviceIdleService.addPowerSaveWhitelistApp(pkg); @@ -155,7 +137,6 @@ public class PowerWhitelistBackend { @VisibleForTesting public void refreshList() { mSysWhitelistedApps.clear(); - mSysWhitelistedAppsExceptIdle.clear(); mWhitelistedApps.clear(); mDefaultActiveApps.clear(); if (mDeviceIdleService == null) { @@ -170,11 +151,6 @@ public class PowerWhitelistBackend { for (String app : sysWhitelistedApps) { mSysWhitelistedApps.add(app); } - final String[] sysWhitelistedAppsExceptIdle = - mDeviceIdleService.getSystemPowerWhitelistExceptIdle(); - for (String app : sysWhitelistedAppsExceptIdle) { - mSysWhitelistedAppsExceptIdle.add(app); - } final boolean hasTelephony = mAppContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_TELEPHONY); final ComponentName defaultSms = SmsApplication.getDefaultSmsApplication(mAppContext, diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java index 3a571f9c24cf..20908925feff 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java @@ -151,27 +151,4 @@ public class PowerWhitelistBackendTest { assertThat(mPowerWhitelistBackend.isSysWhitelisted(PACKAGE_TWO)).isFalse(); assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isFalse(); } - - @Test - public void testIsSystemWhitelistedExceptIdle_onePackage() throws Exception { - doReturn(new String[] {PACKAGE_TWO}).when( - mDeviceIdleService).getSystemPowerWhitelistExceptIdle(); - mPowerWhitelistBackend.refreshList(); - - assertThat(mPowerWhitelistBackend.isSysWhitelistedExceptIdle(PACKAGE_ONE)).isFalse(); - assertThat(mPowerWhitelistBackend.isSysWhitelistedExceptIdle(PACKAGE_TWO)).isTrue(); - } - - @Test - public void testIsSystemWhitelistedExceptIdle_packageArray() throws Exception { - doReturn(new String[] {PACKAGE_TWO}).when( - mDeviceIdleService).getSystemPowerWhitelistExceptIdle(); - mPowerWhitelistBackend.refreshList(); - - final String[] idlePackages = {PACKAGE_ONE, PACKAGE_TWO}; - final String[] normalPackages = {PACKAGE_ONE}; - - assertThat(mPowerWhitelistBackend.isSysWhitelistedExceptIdle(normalPackages)).isFalse(); - assertThat(mPowerWhitelistBackend.isSysWhitelistedExceptIdle(idlePackages)).isTrue(); - } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index 090de223602d..c1543fd91060 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -219,7 +219,9 @@ public class SettingsHelper { */ @VisibleForTesting public String getRealValueForSystemSetting(String setting) { - return Settings.System.getString(mContext.getContentResolver(), + // The real value irrespectively of the original setting's namespace is stored in + // Settings.Secure. + return Settings.Secure.getString(mContext.getContentResolver(), setting + SETTING_ORIGINAL_KEY_SUFFIX); } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 0de890dfabf3..84181c912383 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -263,6 +263,9 @@ <!-- Restore settings (used by QS) even if they have been modified --> <uses-permission android:name="android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE" /> + <!-- Permission to make accessibility service access Bubbles --> + <uses-permission android:name="android.permission.ADD_TRUSTED_DISPLAY" /> + <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" /> diff --git a/packages/SystemUI/res/layout/controls_management_favorites.xml b/packages/SystemUI/res/layout/controls_management_favorites.xml index a0d8ae42f584..4850e7534943 100644 --- a/packages/SystemUI/res/layout/controls_management_favorites.xml +++ b/packages/SystemUI/res/layout/controls_management_favorites.xml @@ -26,6 +26,8 @@ android:layout_height="wrap_content" android:layout_marginTop="@dimen/controls_management_list_margin" android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_marginStart="@dimen/controls_management_status_side_margin" + android:layout_marginEnd="@dimen/controls_management_status_side_margin" android:gravity="center_horizontal" /> diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml index fd9936f6b8ea..acef94315e91 100644 --- a/packages/SystemUI/res/layout/screen_record_dialog.xml +++ b/packages/SystemUI/res/layout/screen_record_dialog.xml @@ -20,111 +20,126 @@ android:orientation="vertical" android:background="@drawable/rounded_bg_full"> - <!-- Header --> - <LinearLayout + <!-- Scrollview is necessary to fit everything in landscape layout --> + <ScrollView android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:gravity="center" - android:padding="@dimen/screenrecord_dialog_padding"> - <ImageView - android:layout_width="@dimen/screenrecord_logo_size" - android:layout_height="@dimen/screenrecord_logo_size" - android:src="@drawable/ic_screenrecord" - android:tint="@color/GM2_red_500" - android:layout_marginBottom="@dimen/screenrecord_dialog_padding"/> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceLarge" - android:text="@string/screenrecord_start_label"/> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/screenrecord_description" - android:textAppearance="?android:attr/textAppearanceSmall" - android:paddingTop="@dimen/screenrecord_dialog_padding" - android:paddingBottom="@dimen/screenrecord_dialog_padding"/> + android:layout_height="wrap_content"> - <!-- Options --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal"> - <ImageView - android:layout_width="@dimen/screenrecord_logo_size" - android:layout_height="@dimen/screenrecord_logo_size" - android:src="@drawable/ic_mic_26dp" - android:tint="@color/GM2_grey_700" - android:layout_gravity="center" - android:layout_weight="0" - android:layout_marginRight="@dimen/screenrecord_dialog_padding"/> - <Spinner - android:id="@+id/screen_recording_options" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:prompt="@string/screenrecord_audio_label"/> - <Switch - android:layout_width="wrap_content" - android:layout_height="48dp" - android:layout_weight="1" - android:layout_gravity="end" - android:id="@+id/screenrecord_audio_switch"/> - </LinearLayout> + android:orientation="vertical"> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal"> - <ImageView - android:layout_width="@dimen/screenrecord_logo_size" - android:layout_height="@dimen/screenrecord_logo_size" - android:src="@drawable/ic_touch" - android:tint="@color/GM2_grey_700" - android:layout_gravity="center" - android:layout_marginRight="@dimen/screenrecord_dialog_padding"/> - <Switch + <!-- Header --> + <LinearLayout android:layout_width="match_parent" - android:layout_height="48dp" - android:id="@+id/screenrecord_taps_switch" - android:text="@string/screenrecord_taps_label" - android:textColor="?android:attr/textColorPrimary" - android:textAppearance="?android:attr/textAppearanceSmall"/> + android:layout_height="match_parent" + android:orientation="vertical" + android:gravity="center" + android:padding="@dimen/screenrecord_dialog_padding"> + <ImageView + android:layout_width="@dimen/screenrecord_logo_size" + android:layout_height="@dimen/screenrecord_logo_size" + android:src="@drawable/ic_screenrecord" + android:tint="@color/GM2_red_500" + android:layout_marginBottom="@dimen/screenrecord_dialog_padding"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceLarge" + android:text="@string/screenrecord_start_label"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/screenrecord_description" + android:textAppearance="?android:attr/textAppearanceSmall" + android:paddingTop="@dimen/screenrecord_dialog_padding" + android:paddingBottom="@dimen/screenrecord_dialog_padding"/> - </LinearLayout> - </LinearLayout> + <!-- Options --> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <ImageView + android:layout_width="@dimen/screenrecord_logo_size" + android:layout_height="@dimen/screenrecord_logo_size" + android:src="@drawable/ic_mic_26dp" + android:tint="@color/GM2_grey_700" + android:layout_gravity="center" + android:layout_weight="0" + android:layout_marginRight="@dimen/screenrecord_dialog_padding"/> + <Spinner + android:id="@+id/screen_recording_options" + android:layout_width="0dp" + android:layout_height="48dp" + android:layout_weight="1" + android:prompt="@string/screenrecord_audio_label"/> + <Switch + android:layout_width="wrap_content" + android:minWidth="48dp" + android:layout_height="48dp" + android:layout_weight="0" + android:layout_gravity="end" + android:contentDescription="@string/screenrecord_audio_label" + android:id="@+id/screenrecord_audio_switch"/> + </LinearLayout> - <!-- hr --> - <View - android:layout_width="match_parent" - android:layout_height="1dp" - android:background="@color/GM2_grey_300"/> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <ImageView + android:layout_width="@dimen/screenrecord_logo_size" + android:layout_height="@dimen/screenrecord_logo_size" + android:src="@drawable/ic_touch" + android:tint="@color/GM2_grey_700" + android:layout_gravity="center" + android:layout_marginRight="@dimen/screenrecord_dialog_padding"/> + <Switch + android:layout_width="match_parent" + android:layout_height="48dp" + android:id="@+id/screenrecord_taps_switch" + android:text="@string/screenrecord_taps_label" + android:textColor="?android:attr/textColorPrimary" + android:textAppearance="?android:attr/textAppearanceSmall"/> - <!-- Buttons --> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:padding="@dimen/screenrecord_dialog_padding"> - <Button - android:id="@+id/button_cancel" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_weight="0" - android:layout_gravity="start" - android:text="@string/cancel" - style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"/> - <Space - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1"/> - <Button - android:id="@+id/button_start" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_weight="0" - android:layout_gravity="end" - android:text="@string/screenrecord_start" - style="@android:style/Widget.DeviceDefault.Button.Colored"/> - </LinearLayout> + </LinearLayout> + </LinearLayout> + + <!-- hr --> + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="@color/GM2_grey_300"/> + + <!-- Buttons --> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:padding="@dimen/screenrecord_dialog_padding"> + <Button + android:id="@+id/button_cancel" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_weight="0" + android:layout_gravity="start" + android:text="@string/cancel" + style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"/> + <Space + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1"/> + <Button + android:id="@+id/button_start" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_weight="0" + android:layout_gravity="end" + android:text="@string/screenrecord_start" + style="@android:style/Widget.DeviceDefault.Button.Colored"/> + </LinearLayout> + </LinearLayout> + </ScrollView> </LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 2f3407d56263..abf460c437ec 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1331,8 +1331,8 @@ <dimen name="controls_management_editing_list_margin">48dp</dimen> <dimen name="controls_management_editing_divider_margin">24dp</dimen> <dimen name="controls_management_apps_extra_side_margin">8dp</dimen> - <dimen name="controls_management_apps_top_margin"></dimen> <dimen name="controls_management_zone_top_margin">32dp</dimen> + <dimen name="controls_management_status_side_margin">16dp</dimen> <dimen name="controls_management_page_indicator_height">24dp</dimen> <dimen name="controls_management_checkbox_size">25dp</dimen> <dimen name="controls_title_size">24sp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 9faa2cfc90bd..efef3f7c91d8 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2739,8 +2739,10 @@ <!-- Controls management favorites screen, See other apps with changes made [CHAR LIMIT=NONE] --> <string name="controls_favorite_toast_no_changes">Changes not saved</string> - <!-- Controls management controls screen error on load message [CHAR LIMIT=60] --> - <string name="controls_favorite_load_error">The list of all controls could not be loaded.</string> + <!-- Controls management controls screen error on load message [CHAR LIMIT=NONE] --> + <string name="controls_favorite_load_error">Controls could not be loaded. Check the <xliff:g id="app" example="System UI">%s</xliff:g> app to make sure that the app settings haven\u2019t changed.</string> + <!-- Controls management controls screen no controls found on load message [CHAR LIMIT=NONE] --> + <string name="controls_favorite_load_none">Compatible controls unavailable</string> <!-- Controls management controls screen header for Other zone [CHAR LIMIT=60] --> <string name="controls_favorite_other_zone_header">Other</string> diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 3490fad844cb..fa567396a3f9 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -747,11 +747,7 @@ public class BubbleExpandedView extends LinearLayout { if (mActivityView == null) { return; } - switch (mActivityViewStatus) { - case INITIALIZED: - case ACTIVITY_STARTED: - mActivityView.release(); - } + mActivityView.release(); if (mTaskId != -1) { try { ActivityTaskManager.getService().removeTask(mTaskId); diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt index 496b21b612fe..4884781c64de 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt @@ -79,6 +79,7 @@ class ControlsFavoritingActivity @Inject constructor( private lateinit var structurePager: ViewPager2 private lateinit var statusText: TextView private lateinit var titleView: TextView + private lateinit var subtitleView: TextView private lateinit var pageIndicator: ManagementPageIndicator private var mTooltipManager: TooltipManager? = null private lateinit var doneButton: View @@ -165,7 +166,12 @@ class ControlsFavoritingActivity @Inject constructor( structurePager.adapter = StructureAdapter(listOfStructures) structurePager.setCurrentItem(structureIndex) if (error) { - statusText.text = resources.getText(R.string.controls_favorite_load_error) + statusText.text = resources.getString(R.string.controls_favorite_load_error, + appName ?: "") + subtitleView.visibility = View.GONE + } else if (listOfStructures.isEmpty()) { + statusText.text = resources.getString(R.string.controls_favorite_load_none) + subtitleView.visibility = View.GONE } else { statusText.visibility = View.GONE } @@ -266,8 +272,9 @@ class ControlsFavoritingActivity @Inject constructor( titleView = requireViewById<TextView>(R.id.title).apply { text = title } - requireViewById<TextView>(R.id.subtitle).text = - resources.getText(R.string.controls_favorite_subtitle) + subtitleView = requireViewById<TextView>(R.id.subtitle).apply { + text = resources.getText(R.string.controls_favorite_subtitle) + } structurePager = requireViewById<ViewPager2>(R.id.structure_pager) structurePager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index d5a28378c993..2df0507a8864 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -96,13 +96,13 @@ public class MediaControlPanel { */ @Inject public MediaControlPanel(Context context, @Background Executor backgroundExecutor, - ActivityStarter activityStarter, MediaHostStatesManager mediaHostStatesManager, + ActivityStarter activityStarter, MediaViewController mediaViewController, SeekBarViewModel seekBarViewModel) { mContext = context; mBackgroundExecutor = backgroundExecutor; mActivityStarter = activityStarter; mSeekBarViewModel = seekBarViewModel; - mMediaViewController = new MediaViewController(context, mediaHostStatesManager); + mMediaViewController = mediaViewController; loadDimens(); } @@ -366,14 +366,6 @@ public class MediaControlPanel { } /** - * Return the token for the current media session - * @return the token - */ - public MediaSession.Token getMediaSessionToken() { - return mToken; - } - - /** * Get the current media controller * @return the controller */ @@ -382,25 +374,6 @@ public class MediaControlPanel { } /** - * Get the name of the package associated with the current media controller - * @return the package name, or null if no controller - */ - public String getMediaPlayerPackage() { - if (mController == null) { - return null; - } - return mController.getPackageName(); - } - - /** - * Check whether this player has an attached media session. - * @return whether there is a controller with a current media session. - */ - public boolean hasMediaSession() { - return mController != null && mController.getPlaybackState() != null; - } - - /** * Check whether the media controlled by this player is currently playing * @return whether it is playing, or false if no controller information */ diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt index 5d28178a3b1b..0b0ffcede3af 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt @@ -25,19 +25,65 @@ import android.media.session.MediaSession data class MediaData( val initialized: Boolean = false, val backgroundColor: Int, + /** + * App name that will be displayed on the player. + */ val app: String?, + /** + * Icon shown on player, close to app name. + */ val appIcon: Drawable?, + /** + * Artist name. + */ val artist: CharSequence?, + /** + * Song name. + */ val song: CharSequence?, + /** + * Album artwork. + */ val artwork: Icon?, + /** + * List of actions that can be performed on the player: prev, next, play, pause, etc. + */ val actions: List<MediaAction>, + /** + * Same as above, but shown on smaller versions of the player, like in QQS or keyguard. + */ val actionsToShowInCompact: List<Int>, + /** + * Package name of the app that's posting the media. + */ val packageName: String, + /** + * Unique media session identifier. + */ val token: MediaSession.Token?, + /** + * Action to perform when the player is tapped. + * This is unrelated to {@link #actions}. + */ val clickIntent: PendingIntent?, + /** + * Where the media is playing: phone, headphones, ear buds, remote session. + */ val device: MediaDeviceData?, + /** + * When active, a player will be displayed on keyguard and quick-quick settings. + * This is unrelated to the stream being playing or not, a player will not be active if + * timed out, or in resumption mode. + */ + var active: Boolean, + /** + * Action that should be performed to restart a non active session. + */ var resumeAction: Runnable?, - val notificationKey: String = "INVALID", + /** + * Notification key for cancelling a media player after a timeout (when not using resumption.) + */ + val notificationKey: String? = null, var hasCheckedForResume: Boolean = false ) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 5fe39958fd67..88bdaece5971 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -67,7 +67,7 @@ private const val LUMINOSITY_THRESHOLD = 0.05f private const val SATURATION_MULTIPLIER = 0.8f private val LOADING = MediaData(false, 0, null, null, null, null, null, - emptyList(), emptyList(), "INVALID", null, null, null, null) + emptyList(), emptyList(), "INVALID", null, null, null, true, null) fun isMediaNotification(sbn: StatusBarNotification): Boolean { if (!sbn.notification.hasMediaSession()) { @@ -88,12 +88,12 @@ fun isMediaNotification(sbn: StatusBarNotification): Boolean { class MediaDataManager @Inject constructor( private val context: Context, private val mediaControllerFactory: MediaControllerFactory, - private val mediaTimeoutListener: MediaTimeoutListener, private val notificationEntryManager: NotificationEntryManager, - private val mediaResumeListener: MediaResumeListener, @Background private val backgroundExecutor: Executor, @Main private val foregroundExecutor: Executor, - private val broadcastDispatcher: BroadcastDispatcher + broadcastDispatcher: BroadcastDispatcher, + mediaTimeoutListener: MediaTimeoutListener, + mediaResumeListener: MediaResumeListener ) { private val listeners: MutableSet<Listener> = mutableSetOf() @@ -131,7 +131,6 @@ class MediaDataManager @Inject constructor( mediaTimeoutListener.timeoutCallback = { token: String, timedOut: Boolean -> setTimedOut(token, timedOut) } addListener(mediaTimeoutListener) - if (useMediaResumption) { mediaResumeListener.addTrackToResumeCallback = { desc: MediaDescription, resumeAction: Runnable, token: MediaSession.Token, appName: String, @@ -215,7 +214,7 @@ class MediaDataManager @Inject constructor( mediaEntries.put(packageName, resumeData) } backgroundExecutor.execute { - loadMediaDataInBg(desc, action, token, appName, appIntent, packageName) + loadMediaDataInBgForResumption(desc, action, token, appName, appIntent, packageName) } } @@ -255,16 +254,21 @@ class MediaDataManager @Inject constructor( fun removeListener(listener: Listener) = listeners.remove(listener) private fun setTimedOut(token: String, timedOut: Boolean) { - if (!timedOut) { - return - } mediaEntries[token]?.let { - notificationEntryManager.removeNotification(it.notificationKey, null /* ranking */, - UNDEFINED_DISMISS_REASON) + if (Utils.useMediaResumption(context)) { + if (it.active == !timedOut) { + return + } + it.active = !timedOut + onMediaDataLoaded(token, token, it) + } else { + notificationEntryManager.removeNotification(it.notificationKey, null /* ranking */, + UNDEFINED_DISMISS_REASON) + } } } - private fun loadMediaDataInBg( + private fun loadMediaDataInBgForResumption( desc: MediaDescription, resumeAction: Runnable, token: MediaSession.Token, @@ -272,11 +276,6 @@ class MediaDataManager @Inject constructor( appIntent: PendingIntent, packageName: String ) { - if (resumeAction == null) { - Log.e(TAG, "Resume action cannot be null") - return - } - if (TextUtils.isEmpty(desc.title)) { Log.e(TAG, "Description incomplete") return @@ -298,8 +297,9 @@ class MediaDataManager @Inject constructor( val mediaAction = getResumeMediaAction(resumeAction) foregroundExecutor.execute { onMediaDataLoaded(packageName, null, MediaData(true, Color.DKGRAY, appName, - null, desc.subtitle, desc.title, artworkIcon, listOf(mediaAction), listOf(0), - packageName, token, appIntent, null, resumeAction, packageName)) + null, desc.subtitle, desc.title, artworkIcon, listOf(mediaAction), listOf(0), + packageName, token, appIntent, device = null, active = false, + resumeAction = resumeAction)) } } @@ -430,7 +430,8 @@ class MediaDataManager @Inject constructor( foregroundExecutor.execute { onMediaDataLoaded(key, oldKey, MediaData(true, bgColor, app, smallIconDrawable, artist, song, artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token, - notif.contentIntent, null, resumeAction, key)) + notif.contentIntent, null, active = true, resumeAction = resumeAction, + notificationKey = key)) } } @@ -528,13 +529,13 @@ class MediaDataManager @Inject constructor( /** * Are there any media notifications active? */ - fun hasActiveMedia() = mediaEntries.any({ isActive(it.value) }) + fun hasActiveMedia() = mediaEntries.any { it.value.active } - fun isActive(data: MediaData): Boolean { - if (data.token == null) { + fun isActive(token: MediaSession.Token?): Boolean { + if (token == null) { return false } - val controller = mediaControllerFactory.create(data.token) + val controller = mediaControllerFactory.create(token) val state = controller?.playbackState?.state return state != null && NotificationMediaManager.isActiveState(state) } @@ -542,7 +543,7 @@ class MediaDataManager @Inject constructor( /** * Are there any media entries, including resume controls? */ - fun hasAnyMedia() = mediaEntries.isNotEmpty() + fun hasAnyMedia() = if (useMediaResumption) mediaEntries.isNotEmpty() else hasActiveMedia() interface Listener { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt index de0af16bc2fa..7ae2dc5c0941 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt @@ -22,6 +22,10 @@ import android.media.session.MediaController import com.android.settingslib.media.LocalMediaManager import com.android.settingslib.media.MediaDevice import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.Dumpable +import com.android.systemui.dump.DumpManager +import java.io.FileDescriptor +import java.io.PrintWriter import java.util.concurrent.Executor import javax.inject.Inject import javax.inject.Singleton @@ -35,13 +39,15 @@ class MediaDeviceManager @Inject constructor( private val localMediaManagerFactory: LocalMediaManagerFactory, private val mr2manager: MediaRouter2Manager, @Main private val fgExecutor: Executor, - private val mediaDataManager: MediaDataManager -) : MediaDataManager.Listener { + private val mediaDataManager: MediaDataManager, + private val dumpManager: DumpManager +) : MediaDataManager.Listener, Dumpable { private val listeners: MutableSet<Listener> = mutableSetOf() private val entries: MutableMap<String, Token> = mutableMapOf() init { mediaDataManager.addListener(this) + dumpManager.registerDumpable(javaClass.name, this) } /** @@ -81,6 +87,17 @@ class MediaDeviceManager @Inject constructor( } } + override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) { + with(pw) { + println("MediaDeviceManager state:") + entries.forEach { + key, entry -> + println(" key=$key") + entry.dump(fd, pw, args) + } + } + } + private fun processDevice(key: String, device: MediaDevice?) { val enabled = device != null val data = MediaDeviceData(enabled, device?.iconWithoutBackground, device?.name) @@ -122,6 +139,17 @@ class MediaDeviceManager @Inject constructor( localMediaManager.stopScan() localMediaManager.unregisterCallback(this) } + fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) { + val route = controller?.let { + mr2manager.getRoutingSessionForMediaController(it) + } + with(pw) { + println(" current device is ${current?.name}") + val type = controller?.playbackInfo?.playbackType + println(" PlaybackType=$type (1 for local, 2 for remote)") + println(" route=$route") + } + } override fun onDeviceListUpdate(devices: List<MediaDevice>?) = fgExecutor.execute { updateCurrent() } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt index 2bd8c0cbeab2..7c5f0d1c2a16 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt @@ -1,5 +1,6 @@ package com.android.systemui.media +import android.graphics.PointF import android.graphics.Rect import android.view.View import android.view.View.OnAttachStateChangeListener @@ -20,8 +21,6 @@ class MediaHost @Inject constructor( var location: Int = -1 private set var visibleChangedListener: ((Boolean) -> Unit)? = null - var visible: Boolean = false - private set private val tmpLocationOnScreen: IntArray = intArrayOf(0, 0) @@ -109,16 +108,17 @@ class MediaHost @Inject constructor( } private fun updateViewVisibility() { - if (showsOnlyActiveMedia) { - visible = mediaDataManager.hasActiveMedia() + visible = if (showsOnlyActiveMedia) { + mediaDataManager.hasActiveMedia() } else { - visible = mediaDataManager.hasAnyMedia() + mediaDataManager.hasAnyMedia() } hostView.visibility = if (visible) View.VISIBLE else View.GONE visibleChangedListener?.invoke(visible) } class MediaHostStateHolder @Inject constructor() : MediaHostState { + private var gonePivot: PointF = PointF() override var measurementInput: MeasurementInput? = null set(value) { @@ -144,6 +144,25 @@ class MediaHost @Inject constructor( } } + override var visible: Boolean = true + set(value) { + if (field == value) { + return + } + field = value + changedListener?.invoke() + } + + override fun getPivotX(): Float = gonePivot.x + override fun getPivotY(): Float = gonePivot.y + override fun setGonePivot(x: Float, y: Float) { + if (gonePivot.equals(x, y)) { + return + } + gonePivot.set(x, y) + changedListener?.invoke() + } + /** * A listener for all changes. This won't be copied over when invoking [copy] */ @@ -157,6 +176,8 @@ class MediaHost @Inject constructor( mediaHostState.expansion = expansion mediaHostState.showsOnlyActiveMedia = showsOnlyActiveMedia mediaHostState.measurementInput = measurementInput?.copy() + mediaHostState.visible = visible + mediaHostState.gonePivot.set(gonePivot) return mediaHostState } @@ -173,6 +194,12 @@ class MediaHost @Inject constructor( if (showsOnlyActiveMedia != other.showsOnlyActiveMedia) { return false } + if (visible != other.visible) { + return false + } + if (!gonePivot.equals(other.getPivotX(), other.getPivotY())) { + return false + } return true } @@ -180,6 +207,8 @@ class MediaHost @Inject constructor( var result = measurementInput?.hashCode() ?: 0 result = 31 * result + expansion.hashCode() result = 31 * result + showsOnlyActiveMedia.hashCode() + result = 31 * result + if (visible) 1 else 2 + result = 31 * result + gonePivot.hashCode() return result } } @@ -194,7 +223,8 @@ interface MediaHostState { var measurementInput: MeasurementInput? /** - * The expansion of the player, 0 for fully collapsed, 1 for fully expanded + * The expansion of the player, 0 for fully collapsed (up to 3 actions), 1 for fully expanded + * (up to 5 actions.) */ var expansion: Float @@ -204,6 +234,30 @@ interface MediaHostState { var showsOnlyActiveMedia: Boolean /** + * If the view should be VISIBLE or GONE. + */ + var visible: Boolean + + /** + * Sets the pivot point when clipping the height or width. + * Clipping happens when animating visibility when we're visible in QS but not on QQS, + * for example. + */ + fun setGonePivot(x: Float, y: Float) + + /** + * x position of pivot, from 0 to 1 + * @see [setGonePivot] + */ + fun getPivotX(): Float + + /** + * y position of pivot, from 0 to 1 + * @see [setGonePivot] + */ + fun getPivotY(): Float + + /** * Get a copy of this view state, deepcopying all appropriate members */ fun copy(): MediaHostState diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt index 3c3f4a977ee7..e0155a1a558f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt @@ -43,6 +43,11 @@ class MediaTimeoutListener @Inject constructor( private val mediaListeners: MutableMap<String, PlaybackStateListener> = mutableMapOf() + /** + * Callback representing that a media object is now expired: + * @param token Media session unique identifier + * @param pauseTimeuot True when expired for {@code PAUSED_MEDIA_TIMEOUT} + */ lateinit var timeoutCallback: (String, Boolean) -> Unit override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { @@ -112,11 +117,11 @@ class MediaTimeoutListener @Inject constructor( } } - private fun expireMediaTimeout(mediaNotificationKey: String, reason: String) { + private fun expireMediaTimeout(mediaKey: String, reason: String) { cancellation?.apply { if (DEBUG) { Log.v(TAG, - "media timeout cancelled for $mediaNotificationKey, reason: $reason") + "media timeout cancelled for $mediaKey, reason: $reason") } run() } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt index e82bb402407e..90ccfc6ca725 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt @@ -17,20 +17,22 @@ package com.android.systemui.media import android.content.Context +import android.graphics.PointF import androidx.constraintlayout.widget.ConstraintSet import com.android.systemui.R +import com.android.systemui.util.animation.MeasurementOutput import com.android.systemui.util.animation.TransitionLayout import com.android.systemui.util.animation.TransitionLayoutController import com.android.systemui.util.animation.TransitionViewState -import com.android.systemui.util.animation.MeasurementOutput +import javax.inject.Inject /** * A class responsible for controlling a single instance of a media player handling interactions * with the view instance and keeping the media view states up to date. */ -class MediaViewController( +class MediaViewController @Inject constructor( context: Context, - val mediaHostStatesManager: MediaHostStatesManager + private val mediaHostStatesManager: MediaHostStatesManager ) { private var firstRefresh: Boolean = true @@ -44,7 +46,7 @@ class MediaViewController( /** * A map containing all viewStates for all locations of this mediaState */ - private val mViewStates: MutableMap<MediaHostState, TransitionViewState?> = mutableMapOf() + private val viewStates: MutableMap<MediaHostState, TransitionViewState?> = mutableMapOf() /** * The ending location of the view where it ends when all animations and transitions have @@ -69,6 +71,11 @@ class MediaViewController( private val tmpState = TransitionViewState() /** + * Temporary variable to avoid unnecessary allocations. + */ + private val tmpPoint = PointF() + + /** * A callback for media state changes */ val stateCallback = object : MediaHostStatesManager.Callback { @@ -125,7 +132,7 @@ class MediaViewController( * it's not available, it will recreate one by measuring, which may be expensive. */ private fun obtainViewState(state: MediaHostState): TransitionViewState? { - val viewState = mViewStates[state] + val viewState = viewStates[state] if (viewState != null) { // we already have cached this measurement, let's continue return viewState @@ -143,7 +150,7 @@ class MediaViewController( // We don't want to cache interpolated or null states as this could quickly fill up // our cache. We only cache the start and the end states since the interpolation // is cheap - mViewStates[state.copy()] = result + viewStates[state.copy()] = result } else { // This is an interpolated state val startState = state.copy().also { it.expansion = 0.0f } @@ -153,11 +160,13 @@ class MediaViewController( val startViewState = obtainViewState(startState) as TransitionViewState val endState = state.copy().also { it.expansion = 1.0f } val endViewState = obtainViewState(endState) as TransitionViewState + tmpPoint.set(startState.getPivotX(), startState.getPivotY()) result = TransitionViewState() layoutController.getInterpolatedState( startViewState, endViewState, state.expansion, + tmpPoint, result) } } else { @@ -213,11 +222,35 @@ class MediaViewController( val shouldAnimate = animateNextStateChange && !applyImmediately + var startHostState = mediaHostStatesManager.mediaHostStates[startLocation] + var endHostState = mediaHostStatesManager.mediaHostStates[endLocation] + var swappedStartState = false + var swappedEndState = false + + // if we're going from or to a non visible state, let's grab the visible one and animate + // the view being clipped instead. + if (endHostState?.visible != true) { + endHostState = startHostState + swappedEndState = true + } + if (startHostState?.visible != true) { + startHostState = endHostState + swappedStartState = true + } + if (startHostState == null || endHostState == null) { + return + } + + var endViewState = obtainViewState(endHostState) ?: return + if (swappedEndState) { + endViewState = endViewState.copy() + endViewState.height = 0 + } + // Obtain the view state that we'd want to be at the end // The view might not be bound yet or has never been measured and in that case will be // reset once the state is fully available - val endState = obtainViewStateForLocation(endLocation) ?: return - layoutController.setMeasureState(endState) + layoutController.setMeasureState(endViewState) // If the view isn't bound, we can drop the animation, otherwise we'll executute it animateNextStateChange = false @@ -225,24 +258,43 @@ class MediaViewController( return } - val startState = obtainViewStateForLocation(startLocation) + var startViewState = obtainViewState(startHostState) + if (swappedStartState) { + startViewState = startViewState?.copy() + startViewState?.height = 0 + } + val result: TransitionViewState? - if (transitionProgress == 1.0f || startState == null) { - result = endState + result = if (transitionProgress == 1.0f || startViewState == null) { + endViewState } else if (transitionProgress == 0.0f) { - result = startState + startViewState } else { - layoutController.getInterpolatedState(startState, endState, transitionProgress, - tmpState) - result = tmpState + if (swappedEndState || swappedStartState) { + tmpPoint.set(startHostState.getPivotX(), startHostState.getPivotY()) + } else { + tmpPoint.set(0.0f, 0.0f) + } + layoutController.getInterpolatedState(startViewState, endViewState, transitionProgress, + tmpPoint, tmpState) + tmpState } layoutController.setState(result, applyImmediately, shouldAnimate, animationDuration, animationDelay) } - private fun obtainViewStateForLocation(location: Int): TransitionViewState? { - val mediaState = mediaHostStatesManager.mediaHostStates[location] ?: return null - return obtainViewState(mediaState) + /** + * Retrieves the [TransitionViewState] and [MediaHostState] of a [@MediaLocation]. + * In the event of [location] not being visible, [locationWhenHidden] will be used instead. + * + * @param location Target + * @param locationWhenHidden Location that will be used when the target is not + * [MediaHost.visible] + * @return State require for executing a transition, and also the respective [MediaHost]. + */ + private fun obtainViewStateForLocation(@MediaLocation location: Int): TransitionViewState? { + val mediaHostState = mediaHostStatesManager.mediaHostStates[location] ?: return null + return obtainViewState(mediaHostState) } /** @@ -250,8 +302,7 @@ class MediaViewController( * This updates the width the view will me measured with. */ fun onLocationPreChange(@MediaLocation newLocation: Int) { - val viewState = obtainViewStateForLocation(newLocation) - viewState?.let { + obtainViewStateForLocation(newLocation)?.let { layoutController.setMeasureState(it) } } @@ -271,7 +322,7 @@ class MediaViewController( fun refreshState() { if (!firstRefresh) { // Let's clear all of our measurements and recreate them! - mViewStates.clear() + viewStates.clear() setCurrentState(currentStartLocation, currentEndLocation, currentTransitionProgress, applyImmediately = false) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 0fc3829fab66..5021e0019d57 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -235,6 +235,8 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne protected void initMediaHostState() { mMediaHost.setExpansion(1.0f); mMediaHost.setShowsOnlyActiveMedia(false); + // Reveal player with some parallax (1.0f would also work) + mMediaHost.setGonePivot(0.0f, 0.8f); mMediaHost.init(MediaHierarchyManager.LOCATION_QS); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index ca3753286bf9..8c1e1dd0cac7 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -69,6 +69,7 @@ import android.util.Log; import android.util.MathUtils; import android.util.Slog; import android.view.Display; +import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.SurfaceControl; @@ -374,6 +375,20 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset // Inflate the screenshot layout mScreenshotLayout = LayoutInflater.from(mContext).inflate(R.layout.global_screenshot, null); + mScreenshotLayout.setOnKeyListener(new View.OnKeyListener() { + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + dismissScreenshot("back pressed", true); + return true; + } + return false; + } + }); + // Get focus so that the key events go to the layout. + mScreenshotLayout.setFocusableInTouchMode(true); + mScreenshotLayout.requestFocus(); + mScreenshotAnimatedView = mScreenshotLayout.findViewById(R.id.global_screenshot_animated_view); mScreenshotAnimatedView.setClipToOutline(true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index bc94cdeba37f..81d0699a29e6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -460,6 +460,18 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } @Override + public void onStartedWakingUp() { + mStatusBar.getNotificationShadeWindowView().getWindowInsetsController() + .setAnimationsDisabled(false); + } + + @Override + public void onStartedGoingToSleep() { + mStatusBar.getNotificationShadeWindowView().getWindowInsetsController() + .setAnimationsDisabled(true); + } + + @Override public void onFinishedGoingToSleep() { mBouncer.onScreenTurnedOff(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java index c929243085d6..2d8784dc41bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java @@ -28,7 +28,7 @@ import android.graphics.PixelFormat; import android.graphics.RecordingCanvas; import android.graphics.drawable.Drawable; import android.os.Handler; -import android.util.Log; +import android.os.Trace; import android.view.RenderNodeAnimator; import android.view.View; import android.view.ViewConfiguration; @@ -74,6 +74,11 @@ public class KeyButtonRipple extends Drawable { private final HashSet<Animator> mRunningAnimations = new HashSet<>(); private final ArrayList<Animator> mTmpArray = new ArrayList<>(); + private final TraceAnimatorListener mExitHwTraceAnimator = + new TraceAnimatorListener("exitHardware"); + private final TraceAnimatorListener mEnterHwTraceAnimator = + new TraceAnimatorListener("enterHardware"); + public enum Type { OVAL, ROUNDED_RECT @@ -221,7 +226,7 @@ public class KeyButtonRipple extends Drawable { @Override public void jumpToCurrentState() { - cancelAnimations("jumpToCurrentState"); + endAnimations("jumpToCurrentState", false /* cancel */); } @Override @@ -235,7 +240,6 @@ public class KeyButtonRipple extends Drawable { } public void setPressed(boolean pressed) { - Log.d("b/63783866", "KeyButtonRipple.setPressed: pressed=" + pressed); if (mDark != mLastDark && pressed) { mRipplePaint = null; mLastDark = mDark; @@ -255,14 +259,19 @@ public class KeyButtonRipple extends Drawable { mHandler.removeCallbacksAndMessages(null); } - private void cancelAnimations(String reason) { - Log.d("b/63783866", "KeyButtonRipple.cancelAnimations: reason=" + reason); + private void endAnimations(String reason, boolean cancel) { + Trace.beginSection("KeyButtonRipple.endAnim: reason=" + reason + " cancel=" + cancel); + Trace.endSection(); mVisible = false; mTmpArray.addAll(mRunningAnimations); int size = mTmpArray.size(); for (int i = 0; i < size; i++) { Animator a = mTmpArray.get(i); - a.cancel(); + if (cancel) { + a.cancel(); + } else { + a.end(); + } } mTmpArray.clear(); mRunningAnimations.clear(); @@ -287,7 +296,7 @@ public class KeyButtonRipple extends Drawable { } private void enterSoftware() { - cancelAnimations("enterSoftware"); + endAnimations("enterSoftware", true /* cancel */); mVisible = true; mGlowAlpha = getMaxGlowAlpha(); ObjectAnimator scaleAnimator = ObjectAnimator.ofFloat(this, "glowScale", @@ -373,8 +382,7 @@ public class KeyButtonRipple extends Drawable { } private void enterHardware() { - Log.d("b/63783866", "enterHardware"); - cancelAnimations("enterHardware"); + endAnimations("enterHardware", true /* cancel */); mVisible = true; mDrawingHardwareGlow = true; setExtendStart(CanvasProperty.createFloat(getExtendSize() / 2)); @@ -391,6 +399,7 @@ public class KeyButtonRipple extends Drawable { endAnim.setDuration(ANIMATION_DURATION_SCALE); endAnim.setInterpolator(mInterpolator); endAnim.addListener(mAnimatorListener); + endAnim.addListener(mEnterHwTraceAnimator); endAnim.setTarget(mTargetView); if (isHorizontal()) { @@ -426,13 +435,13 @@ public class KeyButtonRipple extends Drawable { } private void exitHardware() { - Log.d("b/63783866", "exitHardware"); mPaintProp = CanvasProperty.createPaint(getRipplePaint()); final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPaintProp, RenderNodeAnimator.PAINT_ALPHA, 0); opacityAnim.setDuration(ANIMATION_DURATION_FADE); opacityAnim.setInterpolator(Interpolators.ALPHA_OUT); opacityAnim.addListener(mAnimatorListener); + opacityAnim.addListener(mExitHwTraceAnimator); opacityAnim.setTarget(mTargetView); opacityAnim.start(); @@ -443,16 +452,41 @@ public class KeyButtonRipple extends Drawable { private final AnimatorListenerAdapter mAnimatorListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mRunningAnimations.remove(animation); + if (mRunningAnimations.isEmpty() && !mPressed) { + mVisible = false; + mDrawingHardwareGlow = false; + invalidateSelf(); + } + } + }; + + private static final class TraceAnimatorListener extends AnimatorListenerAdapter { + private final String mName; + TraceAnimatorListener(String name) { + mName = name; + } + + @Override + public void onAnimationStart(Animator animation) { + Trace.beginSection("KeyButtonRipple.start." + mName); + Trace.endSection(); + } + + @Override + public void onAnimationCancel(Animator animation) { + Trace.beginSection("KeyButtonRipple.cancel." + mName); + Trace.endSection(); + } + @Override public void onAnimationEnd(Animator animation) { - mRunningAnimations.remove(animation); - if (mRunningAnimations.isEmpty() && !mPressed) { - mVisible = false; - mDrawingHardwareGlow = false; - invalidateSelf(); - } + Trace.beginSection("KeyButtonRipple.end." + mName); + Trace.endSection(); } - }; + } /** * Interpolator with a smooth log deceleration diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt index 806d9d8e158a..eeca1e38abb0 100644 --- a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt @@ -17,6 +17,7 @@ package com.android.systemui.util.animation import android.content.Context +import android.graphics.PointF import android.graphics.Rect import android.util.AttributeSet import android.view.View @@ -151,6 +152,11 @@ class TransitionLayout @JvmOverloads constructor( val layoutTop = top setLeftTopRightBottom(layoutLeft, layoutTop, layoutLeft + currentState.width, layoutTop + currentState.height) + val bounds = clipBounds ?: Rect() + bounds.set(left, top, right, bottom) + clipBounds = bounds + translationX = currentState.translation.x + translationY = currentState.translation.y } /** @@ -234,11 +240,13 @@ class TransitionViewState { var widgetStates: MutableMap<Int, WidgetState> = mutableMapOf() var width: Int = 0 var height: Int = 0 + val translation = PointF() fun copy(reusedState: TransitionViewState? = null): TransitionViewState { // we need a deep copy of this, so we can't use a data class val copy = reusedState ?: TransitionViewState() copy.width = width copy.height = height + copy.translation.set(translation.x, translation.y) for (entry in widgetStates) { copy.widgetStates[entry.key] = entry.value.copy() } @@ -256,6 +264,7 @@ class TransitionViewState { } width = transitionLayout.measuredWidth height = transitionLayout.measuredHeight + translation.set(0.0f, 0.0f) } } diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt index 9ee141053861..b73aeb30009c 100644 --- a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt +++ b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt @@ -17,6 +17,7 @@ package com.android.systemui.util.animation import android.animation.ValueAnimator +import android.graphics.PointF import android.util.MathUtils import com.android.systemui.Interpolators @@ -43,6 +44,7 @@ open class TransitionLayoutController { private var currentState = TransitionViewState() private var animationStartState: TransitionViewState? = null private var state = TransitionViewState() + private var pivot = PointF() private var animator: ValueAnimator = ValueAnimator.ofFloat(0.0f, 1.0f) init { @@ -63,6 +65,7 @@ open class TransitionLayoutController { startState = animationStartState!!, endState = state, progress = animator.animatedFraction, + pivot = pivot, resultState = currentState) view.setState(currentState) } @@ -75,8 +78,10 @@ open class TransitionLayoutController { startState: TransitionViewState, endState: TransitionViewState, progress: Float, + pivot: PointF, resultState: TransitionViewState ) { + this.pivot.set(pivot) val view = transitionLayout ?: return val childCount = view.childCount for (i in 0 until childCount) { @@ -178,6 +183,8 @@ open class TransitionLayoutController { progress).toInt() height = MathUtils.lerp(startState.height.toFloat(), endState.height.toFloat(), progress).toInt() + translation.x = (endState.width - width) * pivot.x + translation.y = (endState.height - height) * pivot.y } } diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java index 32e3a7fc032e..7b114525adcd 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java +++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java @@ -19,11 +19,13 @@ package com.android.systemui.wm; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; +import android.content.Context; import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; import android.os.Handler; import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Slog; import android.util.SparseArray; import android.view.IDisplayWindowInsetsController; @@ -36,6 +38,7 @@ import android.view.WindowInsets; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; +import com.android.internal.view.IInputMethodManager; import com.android.systemui.TransactionPool; import com.android.systemui.dagger.qualifiers.Main; @@ -352,6 +355,16 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged dispatchEndPositioning(mDisplayId, mCancelled, t); if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) { t.hide(mImeSourceControl.getLeash()); + final IInputMethodManager imms = getImms(); + if (imms != null) { + try { + // Remove the IME surface to make the insets invisible for + // non-client controlled insets. + imms.removeImeSurface(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to remove IME surface.", e); + } + } } t.apply(); mTransactionPool.release(t); @@ -382,9 +395,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged * Called when the IME position is starting to animate. * * @param hiddenTop The y position of the top of the IME surface when it is hidden. - * @param shownTop The y position of the top of the IME surface when it is shown. - * @param showing {@code true} when we are animating from hidden to shown, {@code false} - * when animating from shown to hidden. + * @param shownTop The y position of the top of the IME surface when it is shown. + * @param showing {@code true} when we are animating from hidden to shown, {@code false} + * when animating from shown to hidden. */ default void onImeStartPositioning(int displayId, int hiddenTop, int shownTop, boolean showing, SurfaceControl.Transaction t) {} @@ -406,4 +419,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged default void onImeEndPositioning(int displayId, boolean cancel, SurfaceControl.Transaction t) {} } + + public IInputMethodManager getImms() { + return IInputMethodManager.Stub.asInterface( + ServiceManager.getService(Context.INPUT_METHOD_SERVICE)); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index 737ced63eed0..e6a41fbac3b5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -31,6 +31,7 @@ import android.widget.ImageButton import android.widget.ImageView import android.widget.SeekBar import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.LiveData import androidx.test.filters.SmallTest import com.android.systemui.R @@ -75,9 +76,9 @@ public class MediaControlPanelTest : SysuiTestCase() { @Mock private lateinit var holder: PlayerViewHolder @Mock private lateinit var view: TransitionLayout - @Mock private lateinit var mediaHostStatesManager: MediaHostStatesManager @Mock private lateinit var seekBarViewModel: SeekBarViewModel @Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress> + @Mock private lateinit var mediaViewController: MediaViewController private lateinit var appIcon: ImageView private lateinit var appName: TextView private lateinit var albumView: ImageView @@ -104,8 +105,10 @@ public class MediaControlPanelTest : SysuiTestCase() { @Before fun setUp() { bgExecutor = FakeExecutor(FakeSystemClock()) + whenever(mediaViewController.expandedLayout).thenReturn(mock(ConstraintSet::class.java)) + whenever(mediaViewController.collapsedLayout).thenReturn(mock(ConstraintSet::class.java)) - player = MediaControlPanel(context, bgExecutor, activityStarter, mediaHostStatesManager, + player = MediaControlPanel(context, bgExecutor, activityStarter, mediaViewController, seekBarViewModel) whenever(seekBarViewModel.progress).thenReturn(seekBarData) @@ -172,7 +175,7 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun bindWhenUnattached() { val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), - emptyList(), PACKAGE, null, null, device, null) + emptyList(), PACKAGE, null, null, device, true, null) player.bind(state) assertThat(player.isPlaying()).isFalse() } @@ -181,7 +184,7 @@ public class MediaControlPanelTest : SysuiTestCase() { fun bindText() { player.attach(holder) val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), - emptyList(), PACKAGE, session.getSessionToken(), null, device, null) + emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null) player.bind(state) assertThat(appName.getText()).isEqualTo(APP) assertThat(titleText.getText()).isEqualTo(TITLE) @@ -192,7 +195,7 @@ public class MediaControlPanelTest : SysuiTestCase() { fun bindBackgroundColor() { player.attach(holder) val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), - emptyList(), PACKAGE, session.getSessionToken(), null, device, null) + emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null) player.bind(state) val list = ArgumentCaptor.forClass(ColorStateList::class.java) verify(view).setBackgroundTintList(list.capture()) @@ -203,7 +206,7 @@ public class MediaControlPanelTest : SysuiTestCase() { fun bindDevice() { player.attach(holder) val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), - emptyList(), PACKAGE, session.getSessionToken(), null, device, null) + emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null) player.bind(state) assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME) assertThat(seamless.isEnabled()).isTrue() @@ -213,7 +216,7 @@ public class MediaControlPanelTest : SysuiTestCase() { fun bindDisabledDevice() { player.attach(holder) val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), - emptyList(), PACKAGE, session.getSessionToken(), null, disabledDevice, null) + emptyList(), PACKAGE, session.getSessionToken(), null, disabledDevice, true, null) player.bind(state) assertThat(seamless.isEnabled()).isFalse() assertThat(seamlessText.getText()).isEqualTo(context.getResources().getString( @@ -224,7 +227,7 @@ public class MediaControlPanelTest : SysuiTestCase() { fun bindNullDevice() { player.attach(holder) val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), - emptyList(), PACKAGE, session.getSessionToken(), null, null, null) + emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null) player.bind(state) assertThat(seamless.isEnabled()).isTrue() assertThat(seamlessText.getText()).isEqualTo(context.getResources().getString( diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java index bed5c9eb6df5..618ee892b2b0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java @@ -79,7 +79,8 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { mManager.addListener(mListener); mMediaData = new MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, - new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, null, KEY, false); + new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, KEY, + false); mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt index 6fcf6e37572b..6c7f2e8d7925 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt @@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest import com.android.settingslib.media.LocalMediaManager import com.android.settingslib.media.MediaDevice import com.android.systemui.SysuiTestCase +import com.android.systemui.dump.DumpManager import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock @@ -71,6 +72,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() { @Mock private lateinit var lmm: LocalMediaManager @Mock private lateinit var mr2: MediaRouter2Manager private lateinit var fakeExecutor: FakeExecutor + @Mock private lateinit var dumpster: DumpManager @Mock private lateinit var listener: MediaDeviceManager.Listener @Mock private lateinit var device: MediaDevice @Mock private lateinit var icon: Drawable @@ -85,7 +87,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() { @Before fun setUp() { fakeExecutor = FakeExecutor(FakeSystemClock()) - manager = MediaDeviceManager(context, lmmFactory, mr2, fakeExecutor, mediaDataManager) + manager = MediaDeviceManager(context, lmmFactory, mr2, fakeExecutor, mediaDataManager, + dumpster) manager.addListener(listener) // Configure mocks. @@ -116,7 +119,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() { setStyle(Notification.MediaStyle().setMediaSession(session.getSessionToken())) } mediaData = MediaData(true, 0, PACKAGE, null, null, SESSION_TITLE, null, - emptyList(), emptyList(), PACKAGE, session.sessionToken, null, null, null) + emptyList(), emptyList(), PACKAGE, session.sessionToken, clickIntent = null, + device = null, active = true, resumeAction = null) } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt index 7d44327b0d38..082c8ba8744f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt @@ -93,7 +93,8 @@ class MediaTimeoutListenerTest : SysuiTestCase() { } session.setActive(true) mediaData = MediaData(true, 0, PACKAGE, null, null, SESSION_TITLE, null, - emptyList(), emptyList(), PACKAGE, session.sessionToken, null, null, null) + emptyList(), emptyList(), PACKAGE, session.sessionToken, clickIntent = null, + device = null, active = true, resumeAction = null) } @Test diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java index 0ec8654f2a20..398ece4c1836 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java +++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java @@ -77,8 +77,8 @@ final class AutofillInlineSessionController { if (mSession != null) { // Destroy the existing session. mSession.destroySessionLocked(); - mInlineFillUi = null; } + mInlineFillUi = null; // TODO(b/151123764): consider reusing the same AutofillInlineSession object for the // same field. mSession = new AutofillInlineSuggestionsRequestSession(mInputMethodManagerInternal, mUserId, @@ -87,6 +87,22 @@ final class AutofillInlineSessionController { } /** + * Destroys the current session. May send an empty response to IME to clear the suggestions if + * the focus didn't change to a different field. + * + * @param autofillId the currently focused view from the autofill session + */ + @GuardedBy("mLock") + void destroyLocked(@NonNull AutofillId autofillId) { + if (mSession != null) { + mSession.onInlineSuggestionsResponseLocked(InlineFillUi.emptyUi(autofillId)); + mSession.destroySessionLocked(); + mSession = null; + } + mInlineFillUi = null; + } + + /** * Returns the {@link InlineSuggestionsRequest} provided by IME for the last request. * * <p> The caller is responsible for making sure Autofill hears back from IME before calling diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index a9a0ab69f633..c7490b3b991c 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -3702,6 +3702,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState unlinkClientVultureLocked(); mUi.destroyAll(mPendingSaveUi, this, true); mUi.clearCallback(this); + if (mCurrentViewId != null) { + mInlineSessionController.destroyLocked(mCurrentViewId); + } mDestroyed = true; // Log metrics diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java index 5e10916c4491..a4e58a1faeac 100644 --- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java +++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java @@ -649,16 +649,33 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { mReporter.onStartPackageBackup(PM_PACKAGE); mCurrentPackage = new PackageInfo(); mCurrentPackage.packageName = PM_PACKAGE; - try { - extractPmAgentData(mCurrentPackage); + // If we can't even extractPmAgentData(), then we treat the local state as + // compromised, just in case. This means that we will clear data and will + // start from a clean slate in the next attempt. It's not clear whether that's + // the right thing to do, but matches what we have historically done. + try { + extractPmAgentData(mCurrentPackage); + } catch (TaskException e) { + throw TaskException.stateCompromised(e); // force stateCompromised + } + // During sendDataToTransport, we generally trust any thrown TaskException + // about whether stateCompromised because those are likely transient; + // clearing state for those would have the potential to lead to cascading + // failures, as discussed in http://b/144030477. + // For specific status codes (e.g. TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED), + // cleanUpAgentForTransportStatus() or theoretically handleTransportStatus() + // still have the opportunity to perform additional clean-up tasks. int status = sendDataToTransport(mCurrentPackage); cleanUpAgentForTransportStatus(status); } catch (AgentException | TaskException e) { mReporter.onExtractPmAgentDataError(e); cleanUpAgentForError(e); - // PM agent failure is task failure. - throw TaskException.stateCompromised(e); + if (e instanceof TaskException) { + throw (TaskException) e; + } else { + throw TaskException.stateCompromised(e); // PM agent failure is task failure. + } } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 13e5ab451cfc..4a4b7dd7cc29 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -26,6 +26,15 @@ import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_SECU import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_TRUE; import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED; +import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ACCEPT_DATA_SHARE_REQUEST; +import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_CLIENT_PIPE_FAIL; +import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_CONCURRENT_REQUEST; +import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_EMPTY_DATA; +import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_IOEXCEPTION; +import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_SERVICE_PIPE_FAIL; +import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED; +import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_WRITE_FINISHED; +import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__REJECT_DATA_SHARE_REQUEST; import static com.android.internal.util.SyncResultReceiver.bundleFor; import android.annotation.NonNull; @@ -95,6 +104,7 @@ import java.util.List; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; /** * A service used to observe the contents of the screen. @@ -114,6 +124,14 @@ public final class ContentCaptureManagerService extends private static final int MAX_CONCURRENT_FILE_SHARING_REQUESTS = 10; private static final int DATA_SHARE_BYTE_BUFFER_LENGTH = 1_024; + // Needed to pass checkstyle_hook as names are too long for one line. + private static final int EVENT__DATA_SHARE_ERROR_CONCURRENT_REQUEST = + CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_CONCURRENT_REQUEST; + private static final int EVENT__DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED = + CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED; + private static final int EVENT__DATA_SHARE_WRITE_FINISHED = + CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_WRITE_FINISHED; + private final LocalService mLocalService = new LocalService(); @Nullable @@ -657,6 +675,10 @@ public final class ContentCaptureManagerService extends if (mPackagesWithShareRequests.size() >= MAX_CONCURRENT_FILE_SHARING_REQUESTS || mPackagesWithShareRequests.contains(request.getPackageName())) { try { + String serviceName = mServiceNameResolver.getServiceName(userId); + ContentCaptureMetricsLogger.writeServiceEvent( + EVENT__DATA_SHARE_ERROR_CONCURRENT_REQUEST, + serviceName, request.getPackageName()); clientAdapter.error( ContentCaptureManager.DATA_SHARE_ERROR_CONCURRENT_REQUEST); } catch (RemoteException e) { @@ -920,6 +942,7 @@ public final class ContentCaptureManagerService extends @NonNull private final DataShareRequest mDataShareRequest; @NonNull private final IDataShareWriteAdapter mClientAdapter; @NonNull private final ContentCaptureManagerService mParentService; + @NonNull private final AtomicBoolean mLoggedWriteFinish = new AtomicBoolean(false); DataShareCallbackDelegate(@NonNull DataShareRequest dataShareRequest, @NonNull IDataShareWriteAdapter clientAdapter, @@ -932,9 +955,12 @@ public final class ContentCaptureManagerService extends @Override public void accept(@NonNull IDataShareReadAdapter serviceAdapter) throws RemoteException { Slog.i(TAG, "Data share request accepted by Content Capture service"); + logServiceEvent(CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ACCEPT_DATA_SHARE_REQUEST); Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe(); if (clientPipe == null) { + logServiceEvent( + CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_CLIENT_PIPE_FAIL); mClientAdapter.error(ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN); serviceAdapter.error(ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN); return; @@ -945,6 +971,8 @@ public final class ContentCaptureManagerService extends Pair<ParcelFileDescriptor, ParcelFileDescriptor> servicePipe = createPipe(); if (servicePipe == null) { + logServiceEvent( + CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_SERVICE_PIPE_FAIL); bestEffortCloseFileDescriptors(sourceIn, sinkIn); mClientAdapter.error(ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN); @@ -987,6 +1015,8 @@ public final class ContentCaptureManagerService extends } } catch (IOException e) { Slog.e(TAG, "Failed to pipe client and service streams", e); + logServiceEvent( + CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_IOEXCEPTION); sendErrorSignal(mClientAdapter, serviceAdapter, ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN); @@ -996,6 +1026,10 @@ public final class ContentCaptureManagerService extends .remove(mDataShareRequest.getPackageName()); } if (receivedData) { + if (!mLoggedWriteFinish.get()) { + logServiceEvent(EVENT__DATA_SHARE_WRITE_FINISHED); + mLoggedWriteFinish.set(true); + } try { mClientAdapter.finish(); } catch (RemoteException e) { @@ -1008,6 +1042,8 @@ public final class ContentCaptureManagerService extends } } else { // Client or service may have crashed before sending. + logServiceEvent( + CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_EMPTY_DATA); sendErrorSignal(mClientAdapter, serviceAdapter, ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN); } @@ -1027,6 +1063,7 @@ public final class ContentCaptureManagerService extends @Override public void reject() throws RemoteException { Slog.i(TAG, "Data share request rejected by Content Capture service"); + logServiceEvent(CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__REJECT_DATA_SHARE_REQUEST); mClientAdapter.rejected(); } @@ -1048,11 +1085,16 @@ public final class ContentCaptureManagerService extends && !sourceOut.getFileDescriptor().valid(); if (finishedSuccessfully) { + if (!mLoggedWriteFinish.get()) { + logServiceEvent(EVENT__DATA_SHARE_WRITE_FINISHED); + mLoggedWriteFinish.set(true); + } Slog.i(TAG, "Content capture data sharing session terminated " + "successfully for package '" + mDataShareRequest.getPackageName() + "'"); } else { + logServiceEvent(EVENT__DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED); Slog.i(TAG, "Reached the timeout of Content Capture data sharing session " + "for package '" + mDataShareRequest.getPackageName() @@ -1123,5 +1165,12 @@ public final class ContentCaptureManagerService extends Slog.e(TAG, "Failed to call error() the service operation", e); } } + + private void logServiceEvent(int eventType) { + int userId = UserHandle.getCallingUserId(); + String serviceName = mParentService.mServiceNameResolver.getServiceName(userId); + ContentCaptureMetricsLogger.writeServiceEvent(eventType, serviceName, + mDataShareRequest.getPackageName()); + } } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java index 0b9bf3962562..08e6a0550f69 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java @@ -156,6 +156,9 @@ final class RemoteContentCaptureService public void onDataShareRequest(@NonNull DataShareRequest request, @NonNull IDataShareCallback.Stub dataShareCallback) { scheduleAsyncRequest((s) -> s.onDataShared(request, dataShareCallback)); + writeServiceEvent( + FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_DATA_SHARE_REQUEST, + mComponentName, request.getPackageName()); } /** diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 979dcc8de2a1..ed67f210b063 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -405,9 +405,7 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -8436,7 +8434,9 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); if (!isDebuggable) { - if (!app.isProfileableByShell()) { + boolean isAppDebuggable = (app.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; + boolean isAppProfileable = app.isProfileableByShell(); + if (!isAppDebuggable && !isAppProfileable) { throw new SecurityException("Process not debuggable, " + "and not profileable by shell: " + app.packageName); } diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java index a0349493edbc..a168af5ad842 100644 --- a/services/core/java/com/android/server/am/ProcessStatsService.java +++ b/services/core/java/com/android/server/am/ProcessStatsService.java @@ -1269,12 +1269,12 @@ public final class ProcessStatsService extends IProcessStats.Stub { * Dump proto for the statsd, mainly for testing. */ private void dumpProtoForStatsd(FileDescriptor fd) { - final ProtoOutputStream proto = new ProtoOutputStream(fd); + final ProtoOutputStream[] protos = {new ProtoOutputStream(fd)}; ProcessStats procStats = new ProcessStats(false); getCommittedStatsMerged(0, 0, true, null, procStats); - procStats.dumpAggregatedProtoForStatsd(proto); + procStats.dumpAggregatedProtoForStatsd(protos, 999999 /* max bytes per shard */); - proto.flush(); + protos[0].flush(); } } diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index 18adc0ba27ee..d4377e4870a5 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -116,12 +116,20 @@ final class DisplayDeviceInfo { /** * Flag: This flag identifies secondary displays that should show system decorations, such as * status bar, navigation bar, home activity or IME. + * <p>Note that this flag doesn't work without {@link #FLAG_TRUSTED}</p> * @hide */ // TODO (b/114338689): Remove the flag and use IWindowManager#setShouldShowSystemDecors public static final int FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 12; /** + * Flag: The display is trusted to show system decorations and receive inputs without users' + * touch. + * @see #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS + */ + public static final int FLAG_TRUSTED = 1 << 13; + + /** * Touch attachment: Display does not receive touch. */ public static final int TOUCH_NONE = 0; diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 7de277d80b57..a00c22a409e9 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -16,6 +16,7 @@ package com.android.server.display; +import static android.Manifest.permission.ADD_TRUSTED_DISPLAY; import static android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT; import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; @@ -25,6 +26,7 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_C import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL; @@ -2189,16 +2191,25 @@ public final class DisplayManagerService extends SystemService { } } + if (callingUid == Process.SYSTEM_UID + || checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) { + flags |= VIRTUAL_DISPLAY_FLAG_TRUSTED; + } else { + flags &= ~VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; + } + // Sometimes users can have sensitive information in system decoration windows. An app // could create a virtual display with system decorations support and read the user info // from the surface. // We should only allow adding flag VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS - // to virtual displays that are owned by the system. - if (callingUid != Process.SYSTEM_UID - && (flags & VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) { - if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "createVirtualDisplay()")) { + // to trusted virtual displays. + final int trustedDisplayWithSysDecorFlag = + (VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS + | VIRTUAL_DISPLAY_FLAG_TRUSTED); + if ((flags & trustedDisplayWithSysDecorFlag) + == VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS + && !checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "createVirtualDisplay()")) { throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); - } } final long token = Binder.clearCallingIdentity(); diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 51b656991b63..dde3b07053b2 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -577,6 +577,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInfo.name = getContext().getResources().getString( com.android.internal.R.string.display_manager_hdmi_display_name); } + // The display is trusted since it is created by system. + mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED; } return mInfo; } diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 0261f388f7cb..8556f084a072 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -269,6 +269,9 @@ final class LogicalDisplay { if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) { mBaseDisplayInfo.flags |= Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; } + if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_TRUSTED) != 0) { + mBaseDisplayInfo.flags |= Display.FLAG_TRUSTED; + } Rect maskingInsets = getMaskingInsets(deviceInfo); int maskedWidth = deviceInfo.width - maskingInsets.left - maskingInsets.right; int maskedHeight = deviceInfo.height - maskingInsets.top - maskingInsets.bottom; diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java index 8fb384070e25..69943e3904ed 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java +++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java @@ -16,6 +16,8 @@ package com.android.server.display; +import static com.android.server.display.DisplayDeviceInfo.FLAG_TRUSTED; + import android.annotation.Nullable; import android.content.Context; import android.database.ContentObserver; @@ -356,6 +358,8 @@ final class OverlayDisplayAdapter extends DisplayAdapter { mInfo.type = Display.TYPE_OVERLAY; mInfo.touch = DisplayDeviceInfo.TOUCH_VIRTUAL; mInfo.state = mState; + // The display is trusted since it is created by system. + mInfo.flags |= FLAG_TRUSTED; } return mInfo; } diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index ccd88483593a..210d2979c807 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -25,6 +25,9 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTAT import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; + +import static com.android.server.display.DisplayDeviceInfo.FLAG_TRUSTED; import android.content.Context; import android.hardware.display.IVirtualDisplayCallback; @@ -412,6 +415,9 @@ public class VirtualDisplayAdapter extends DisplayAdapter { if ((mFlags & VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) { mInfo.flags |= DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; } + if ((mFlags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) { + mInfo.flags |= FLAG_TRUSTED; + } mInfo.type = Display.TYPE_VIRTUAL; mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ? diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java index 5584dcf69f50..57323170b327 100644 --- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java +++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java @@ -651,6 +651,8 @@ final class WifiDisplayAdapter extends DisplayAdapter { mInfo.address = mAddress; mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL; mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight); + // The display is trusted since it is created by system. + mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED; } return mInfo; } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 65a13016c9b6..b647a1ab9873 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -207,6 +207,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035; static final int MSG_INITIALIZE_IME = 1040; static final int MSG_CREATE_SESSION = 1050; + static final int MSG_REMOVE_IME_SURFACE = 1060; static final int MSG_START_INPUT = 2000; @@ -3946,6 +3947,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + @Override + public void removeImeSurface() { + mContext.enforceCallingPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW, null); + mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE)); + } + @BinderThread private void notifyUserAction(@NonNull IBinder token) { if (DEBUG) { @@ -4216,6 +4223,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub args.recycle(); return true; } + case MSG_REMOVE_IME_SURFACE: { + try { + if (mEnabledSession != null && mEnabledSession.session != null) { + mEnabledSession.session.removeImeSurface(); + } + } catch (RemoteException e) { + } + return true; + } // --------------------------------------------------------- case MSG_START_INPUT: { diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 6c415ca326c6..33c78e403ce3 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -1463,6 +1463,12 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override + public void removeImeSurface() { + reportNotSupported(); + } + + @BinderThread + @Override public boolean showSoftInput( IInputMethodClient client, IBinder token, int flags, ResultReceiver resultReceiver) { diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 5b9db64dd9b1..4bd99f951cfa 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -19,6 +19,8 @@ package com.android.server.pm; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE; +import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -27,6 +29,7 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageParser; +import android.content.pm.UserInfo; import android.content.pm.parsing.component.ParsedComponent; import android.content.pm.parsing.component.ParsedInstrumentation; import android.content.pm.parsing.component.ParsedIntentInfo; @@ -108,12 +111,25 @@ public class AppsFilter { private final boolean mSystemAppsQueryable; private final FeatureConfig mFeatureConfig; - private final OverlayReferenceMapper mOverlayReferenceMapper; + private final StateProvider mStateProvider; + private PackageParser.SigningDetails mSystemSigningDetails; private Set<String> mProtectedBroadcasts = new ArraySet<>(); - AppsFilter(FeatureConfig featureConfig, String[] forceQueryableWhitelist, + /** + * This structure maps uid -> uid and indicates whether access from the first should be + * filtered to the second. It's essentially a cache of the + * {@link #shouldFilterApplicationInternal(int, SettingBase, PackageSetting, int)} call. + * NOTE: It can only be relied upon after the system is ready to avoid unnecessary update on + * initial scam and is null until {@link #onSystemReady()} is called. + */ + private volatile SparseArray<SparseBooleanArray> mShouldFilterCache; + + @VisibleForTesting(visibility = PRIVATE) + AppsFilter(StateProvider stateProvider, + FeatureConfig featureConfig, + String[] forceQueryableWhitelist, boolean systemAppsQueryable, @Nullable OverlayReferenceMapper.Provider overlayProvider) { mFeatureConfig = featureConfig; @@ -121,8 +137,23 @@ public class AppsFilter { mSystemAppsQueryable = systemAppsQueryable; mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/, overlayProvider); + mStateProvider = stateProvider; } + /** + * Provides system state to AppsFilter via {@link CurrentStateCallback} after properly guarding + * the data with the package lock. + */ + @VisibleForTesting(visibility = PRIVATE) + public interface StateProvider { + void runWithState(CurrentStateCallback callback); + + interface CurrentStateCallback { + void currentState(ArrayMap<String, PackageSetting> settings, UserInfo[] users); + } + } + + @VisibleForTesting(visibility = PRIVATE) public interface FeatureConfig { /** Called when the system is ready and components can be queried. */ @@ -139,6 +170,7 @@ public class AppsFilter { /** * Turns on logging for the given appId + * * @param enable true if logging should be enabled, false if disabled. */ void enableLogging(int appId, boolean enable); @@ -146,6 +178,7 @@ public class AppsFilter { /** * Initializes the package enablement state for the given package. This gives opportunity * to do any expensive operations ahead of the actual checks. + * * @param removed true if adding, false if removing */ void updatePackageState(PackageSetting setting, boolean removed); @@ -161,6 +194,7 @@ public class AppsFilter { @Nullable private SparseBooleanArray mLoggingEnabled = null; + private AppsFilter mAppsFilter; private FeatureConfigImpl( PackageManagerInternal pmInternal, PackageManagerService.Injector injector) { @@ -168,6 +202,10 @@ public class AppsFilter { mInjector = injector; } + public void setAppsFilter(AppsFilter filter) { + mAppsFilter = filter; + } + @Override public void onSystemReady() { mFeatureEnabled = DeviceConfig.getBoolean( @@ -235,6 +273,7 @@ public class AppsFilter { @Override public void onCompatChange(String packageName) { updateEnabledState(mPmInternal.getPackage(packageName)); + mAppsFilter.updateShouldFilterCacheForPackage(packageName); } private void updateEnabledState(AndroidPackage pkg) { @@ -267,7 +306,7 @@ public class AppsFilter { final boolean forceSystemAppsQueryable = injector.getContext().getResources() .getBoolean(R.bool.config_forceSystemPackagesQueryable); - final FeatureConfig featureConfig = new FeatureConfigImpl(pms, injector); + final FeatureConfigImpl featureConfig = new FeatureConfigImpl(pms, injector); final String[] forcedQueryablePackageNames; if (forceSystemAppsQueryable) { // all system apps already queryable, no need to read and parse individual exceptions @@ -280,8 +319,16 @@ public class AppsFilter { forcedQueryablePackageNames[i] = forcedQueryablePackageNames[i].intern(); } } - return new AppsFilter(featureConfig, forcedQueryablePackageNames, - forceSystemAppsQueryable, null); + final StateProvider stateProvider = command -> { + synchronized (injector.getLock()) { + command.currentState(injector.getSettings().mPackages, + injector.getUserManagerInternal().getUserInfos()); + } + }; + AppsFilter appsFilter = new AppsFilter(stateProvider, featureConfig, + forcedQueryablePackageNames, forceSystemAppsQueryable, null); + featureConfig.setAppsFilter(appsFilter); + return appsFilter; } public FeatureConfig getFeatureConfig() { @@ -404,27 +451,59 @@ public class AppsFilter { * visibility of the caller from the target. * * @param recipientUid the uid gaining visibility of the {@code visibleUid}. - * @param visibleUid the uid becoming visible to the {@recipientUid} + * @param visibleUid the uid becoming visible to the {@recipientUid} */ public void grantImplicitAccess(int recipientUid, int visibleUid) { - if (recipientUid != visibleUid - && mImplicitlyQueryable.add(recipientUid, visibleUid) && DEBUG_LOGGING) { - Slog.i(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid); + if (recipientUid != visibleUid) { + if (mImplicitlyQueryable.add(recipientUid, visibleUid) && DEBUG_LOGGING) { + Slog.i(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid); + } + if (mShouldFilterCache != null) { + // update the cache in a one-off manner since we've got all the information we need. + SparseBooleanArray visibleUids = mShouldFilterCache.get(recipientUid); + if (visibleUids == null) { + visibleUids = new SparseBooleanArray(); + mShouldFilterCache.put(recipientUid, visibleUids); + } + visibleUids.put(visibleUid, false); + } } } public void onSystemReady() { + mStateProvider.runWithState(new StateProvider.CurrentStateCallback() { + @Override + public void currentState(ArrayMap<String, PackageSetting> settings, + UserInfo[] users) { + mShouldFilterCache = new SparseArray<>(users.length * settings.size()); + } + }); mFeatureConfig.onSystemReady(); mOverlayReferenceMapper.rebuildIfDeferred(); + updateEntireShouldFilterCache(); } /** * Adds a package that should be considered when filtering visibility between apps. * - * @param newPkgSetting the new setting being added - * @param existingSettings all other settings currently on the device. + * @param newPkgSetting the new setting being added */ - public void addPackage(PackageSetting newPkgSetting, + public void addPackage(PackageSetting newPkgSetting) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "filter.addPackage"); + try { + mStateProvider.runWithState((settings, users) -> { + addPackageInternal(newPkgSetting, settings); + if (mShouldFilterCache != null) { + updateShouldFilterCacheForPackage( + null, newPkgSetting, settings, users, settings.size()); + } // else, rebuild entire cache when system is ready + }); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + private void addPackageInternal(PackageSetting newPkgSetting, ArrayMap<String, PackageSetting> existingSettings) { if (Objects.equals("android", newPkgSetting.name)) { // let's set aside the framework signatures @@ -438,79 +517,153 @@ public class AppsFilter { } } - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "filter.addPackage"); - try { - final AndroidPackage newPkg = newPkgSetting.pkg; - if (newPkg == null) { - // nothing to add - return; - } + final AndroidPackage newPkg = newPkgSetting.pkg; + if (newPkg == null) { + // nothing to add + return; + } - if (!newPkg.getProtectedBroadcasts().isEmpty()) { - mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts()); - recomputeComponentVisibility(existingSettings, newPkg.getPackageName()); - } + if (!newPkg.getProtectedBroadcasts().isEmpty()) { + mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts()); + recomputeComponentVisibility(existingSettings, newPkg.getPackageName()); + } - final boolean newIsForceQueryable = - mForceQueryable.contains(newPkgSetting.appId) - /* shared user that is already force queryable */ - || newPkg.isForceQueryable() - || newPkgSetting.forceQueryableOverride - || (newPkgSetting.isSystem() && (mSystemAppsQueryable - || ArrayUtils.contains(mForceQueryableByDevicePackageNames, - newPkg.getPackageName()))); - if (newIsForceQueryable - || (mSystemSigningDetails != null - && isSystemSigned(mSystemSigningDetails, newPkgSetting))) { - mForceQueryable.add(newPkgSetting.appId); - } + final boolean newIsForceQueryable = + mForceQueryable.contains(newPkgSetting.appId) + /* shared user that is already force queryable */ + || newPkg.isForceQueryable() + || newPkgSetting.forceQueryableOverride + || (newPkgSetting.isSystem() && (mSystemAppsQueryable + || ArrayUtils.contains(mForceQueryableByDevicePackageNames, + newPkg.getPackageName()))); + if (newIsForceQueryable + || (mSystemSigningDetails != null + && isSystemSigned(mSystemSigningDetails, newPkgSetting))) { + mForceQueryable.add(newPkgSetting.appId); + } - for (int i = existingSettings.size() - 1; i >= 0; i--) { - final PackageSetting existingSetting = existingSettings.valueAt(i); - if (existingSetting.appId == newPkgSetting.appId || existingSetting.pkg == null) { - continue; + for (int i = existingSettings.size() - 1; i >= 0; i--) { + final PackageSetting existingSetting = existingSettings.valueAt(i); + if (existingSetting.appId == newPkgSetting.appId || existingSetting.pkg == null) { + continue; + } + final AndroidPackage existingPkg = existingSetting.pkg; + // let's evaluate the ability of already added packages to see this new package + if (!newIsForceQueryable) { + if (canQueryViaComponents(existingPkg, newPkg, mProtectedBroadcasts)) { + mQueriesViaComponent.add(existingSetting.appId, newPkgSetting.appId); } - final AndroidPackage existingPkg = existingSetting.pkg; - // let's evaluate the ability of already added packages to see this new package - if (!newIsForceQueryable) { - if (canQueryViaComponents(existingPkg, newPkg, mProtectedBroadcasts)) { - mQueriesViaComponent.add(existingSetting.appId, newPkgSetting.appId); - } - if (canQueryViaPackage(existingPkg, newPkg) - || canQueryAsInstaller(existingSetting, newPkg)) { - mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId); - } + if (canQueryViaPackage(existingPkg, newPkg) + || canQueryAsInstaller(existingSetting, newPkg)) { + mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId); } - // now we'll evaluate our new package's ability to see existing packages - if (!mForceQueryable.contains(existingSetting.appId)) { - if (canQueryViaComponents(newPkg, existingPkg, mProtectedBroadcasts)) { - mQueriesViaComponent.add(newPkgSetting.appId, existingSetting.appId); - } - if (canQueryViaPackage(newPkg, existingPkg) - || canQueryAsInstaller(newPkgSetting, existingPkg)) { - mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId); - } + } + // now we'll evaluate our new package's ability to see existing packages + if (!mForceQueryable.contains(existingSetting.appId)) { + if (canQueryViaComponents(newPkg, existingPkg, mProtectedBroadcasts)) { + mQueriesViaComponent.add(newPkgSetting.appId, existingSetting.appId); } - // if either package instruments the other, mark both as visible to one another - if (pkgInstruments(newPkgSetting, existingSetting) - || pkgInstruments(existingSetting, newPkgSetting)) { + if (canQueryViaPackage(newPkg, existingPkg) + || canQueryAsInstaller(newPkgSetting, existingPkg)) { mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId); - mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId); } } + // if either package instruments the other, mark both as visible to one another + if (pkgInstruments(newPkgSetting, existingSetting) + || pkgInstruments(existingSetting, newPkgSetting)) { + mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId); + mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId); + } + } + + int existingSize = existingSettings.size(); + ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize); + for (int index = 0; index < existingSize; index++) { + PackageSetting pkgSetting = existingSettings.valueAt(index); + if (pkgSetting.pkg != null) { + existingPkgs.put(pkgSetting.name, pkgSetting.pkg); + } + } + mOverlayReferenceMapper.addPkg(newPkgSetting.pkg, existingPkgs); + mFeatureConfig.updatePackageState(newPkgSetting, false /*removed*/); + } - int existingSize = existingSettings.size(); - ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize); - for (int index = 0; index < existingSize; index++) { - PackageSetting pkgSetting = existingSettings.valueAt(index); - if (pkgSetting.pkg != null) { - existingPkgs.put(pkgSetting.name, pkgSetting.pkg); + private void removeAppIdFromVisibilityCache(int appId) { + if (mShouldFilterCache == null) { + return; + } + for (int i = mShouldFilterCache.size() - 1; i >= 0; i--) { + if (UserHandle.getAppId(mShouldFilterCache.keyAt(i)) == appId) { + mShouldFilterCache.removeAt(i); + continue; + } + SparseBooleanArray targetSparseArray = mShouldFilterCache.valueAt(i); + for (int j = targetSparseArray.size() - 1; j >= 0; j--) { + if (UserHandle.getAppId(targetSparseArray.keyAt(j)) == appId) { + targetSparseArray.removeAt(j); + } + } + } + } + + private void updateEntireShouldFilterCache() { + mStateProvider.runWithState((settings, users) -> { + mShouldFilterCache.clear(); + for (int i = settings.size() - 1; i >= 0; i--) { + updateShouldFilterCacheForPackage( + null /*skipPackage*/, settings.valueAt(i), settings, users, i); + } + }); + } + + public void onUsersChanged() { + if (mShouldFilterCache != null) { + updateEntireShouldFilterCache(); + } + } + + private void updateShouldFilterCacheForPackage(String packageName) { + mStateProvider.runWithState((settings, users) -> { + updateShouldFilterCacheForPackage(null /* skipPackage */, settings.get(packageName), + settings, users, settings.size() /*maxIndex*/); + }); + + } + + private void updateShouldFilterCacheForPackage(@Nullable String skipPackageName, + PackageSetting subjectSetting, ArrayMap<String, PackageSetting> allSettings, + UserInfo[] allUsers, int maxIndex) { + for (int i = Math.min(maxIndex, allSettings.size() - 1); i >= 0; i--) { + PackageSetting otherSetting = allSettings.valueAt(i); + if (subjectSetting.appId == otherSetting.appId) { + continue; + } + //noinspection StringEquality + if (subjectSetting.name == skipPackageName || otherSetting.name == skipPackageName) { + continue; + } + final int userCount = allUsers.length; + final int appxUidCount = userCount * allSettings.size(); + for (int su = 0; su < userCount; su++) { + int subjectUser = allUsers[su].id; + for (int ou = su; ou < userCount; ou++) { + int otherUser = allUsers[ou].id; + int subjectUid = UserHandle.getUid(subjectUser, subjectSetting.appId); + if (!mShouldFilterCache.contains(subjectUid)) { + mShouldFilterCache.put(subjectUid, new SparseBooleanArray(appxUidCount)); + } + int otherUid = UserHandle.getUid(otherUser, otherSetting.appId); + if (!mShouldFilterCache.contains(otherUid)) { + mShouldFilterCache.put(otherUid, new SparseBooleanArray(appxUidCount)); + } + mShouldFilterCache.get(subjectUid).put(otherUid, + shouldFilterApplicationInternal( + subjectUid, subjectSetting, otherSetting, otherUser)); + mShouldFilterCache.get(otherUid).put(subjectUid, + shouldFilterApplicationInternal( + otherUid, otherSetting, subjectSetting, subjectUser)); } } - mOverlayReferenceMapper.addPkg(newPkgSetting.pkg, existingPkgs); - mFeatureConfig.updatePackageState(newPkgSetting, false /*removed*/); - } finally { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } @@ -561,6 +714,7 @@ public class AppsFilter { } } } + /** * Fetches all app Ids that a given setting is currently visible to, per provided user. This * only includes UIDs >= {@link Process#FIRST_APPLICATION_UID} as all other UIDs can already see @@ -569,11 +723,11 @@ public class AppsFilter { * If the setting is visible to all UIDs, null is returned. If an app is not visible to any * applications, the int array will be empty. * - * @param users the set of users that should be evaluated for this calculation + * @param users the set of users that should be evaluated for this calculation * @param existingSettings the set of all package settings that currently exist on device * @return a SparseArray mapping userIds to a sorted int array of appIds that may view the - * provided setting or null if the app is visible to all and no whitelist should be - * applied. + * provided setting or null if the app is visible to all and no whitelist should be + * applied. */ @Nullable public SparseArray<int[]> getVisibilityWhitelist(PackageSetting setting, int[] users, @@ -618,52 +772,61 @@ public class AppsFilter { /** * Removes a package for consideration when filtering visibility between apps. * - * @param setting the setting of the package being removed. - * @param allUsers array of all current users on device. + * @param setting the setting of the package being removed. */ - public void removePackage(PackageSetting setting, int[] allUsers, - ArrayMap<String, PackageSetting> existingSettings) { - mForceQueryable.remove(setting.appId); - - for (int u = 0; u < allUsers.length; u++) { - final int userId = allUsers[u]; - final int removingUid = UserHandle.getUid(userId, setting.appId); - mImplicitlyQueryable.remove(removingUid); - for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) { - mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), removingUid); + public void removePackage(PackageSetting setting) { + removeAppIdFromVisibilityCache(setting.appId); + mStateProvider.runWithState((settings, users) -> { + final int userCount = users.length; + for (int u = 0; u < userCount; u++) { + final int userId = users[u].id; + final int removingUid = UserHandle.getUid(userId, setting.appId); + mImplicitlyQueryable.remove(removingUid); + for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) { + mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), removingUid); + } } - } - mQueriesViaComponent.remove(setting.appId); - for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) { - mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), setting.appId); - } - mQueriesViaPackage.remove(setting.appId); - for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) { - mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.appId); - } + mQueriesViaComponent.remove(setting.appId); + for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) { + mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), setting.appId); + } + mQueriesViaPackage.remove(setting.appId); + for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) { + mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.appId); + } - // re-add other shared user members to re-establish visibility between them and other - // packages - if (setting.sharedUser != null) { - for (int i = setting.sharedUser.packages.size() - 1; i >= 0; i--) { - if (setting.sharedUser.packages.valueAt(i) == setting) { - continue; + // re-add other shared user members to re-establish visibility between them and other + // packages + if (setting.sharedUser != null) { + for (int i = setting.sharedUser.packages.size() - 1; i >= 0; i--) { + if (setting.sharedUser.packages.valueAt(i) == setting) { + continue; + } + addPackageInternal( + setting.sharedUser.packages.valueAt(i), settings); } - addPackage(setting.sharedUser.packages.valueAt(i), existingSettings); } - } - if (!setting.pkg.getProtectedBroadcasts().isEmpty()) { - final String removingPackageName = setting.pkg.getPackageName(); - mProtectedBroadcasts.clear(); - mProtectedBroadcasts.addAll( - collectProtectedBroadcasts(existingSettings, removingPackageName)); - recomputeComponentVisibility(existingSettings, removingPackageName); - } + if (!setting.pkg.getProtectedBroadcasts().isEmpty()) { + final String removingPackageName = setting.pkg.getPackageName(); + mProtectedBroadcasts.clear(); + mProtectedBroadcasts.addAll( + collectProtectedBroadcasts(settings, removingPackageName)); + recomputeComponentVisibility(settings, removingPackageName); + } + + mOverlayReferenceMapper.removePkg(setting.name); + mFeatureConfig.updatePackageState(setting, true /*removed*/); + + if (mShouldFilterCache != null) { + updateShouldFilterCacheForPackage( + setting.name, setting, settings, users, settings.size()); + } + }); + mForceQueryable.remove(setting.appId); + - mOverlayReferenceMapper.removePkg(setting.name); - mFeatureConfig.updatePackageState(setting, true /*removed*/); } /** @@ -680,11 +843,32 @@ public class AppsFilter { PackageSetting targetPkgSetting, int userId) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplication"); try { - - if (!shouldFilterApplicationInternal( - callingUid, callingSetting, targetPkgSetting, userId)) { + if (callingUid < Process.FIRST_APPLICATION_UID + || UserHandle.getAppId(callingUid) == targetPkgSetting.appId) { return false; } + if (mShouldFilterCache != null) { // use cache + SparseBooleanArray shouldFilterTargets = mShouldFilterCache.get(callingUid); + final int targetUid = UserHandle.getUid(userId, targetPkgSetting.appId); + if (shouldFilterTargets == null) { + Slog.wtf(TAG, "Encountered calling uid with no cached rules: " + callingUid); + return true; + } + int indexOfTargetUid = shouldFilterTargets.indexOfKey(targetUid); + if (indexOfTargetUid < 0) { + Slog.w(TAG, "Encountered calling -> target with no cached rules: " + + callingUid + " -> " + targetUid); + return true; + } + if (!shouldFilterTargets.valueAt(indexOfTargetUid)) { + return false; + } + } else { + if (!shouldFilterApplicationInternal( + callingUid, callingSetting, targetPkgSetting, userId)) { + return false; + } + } if (DEBUG_LOGGING || mFeatureConfig.isLoggingEnabled(UserHandle.getAppId(callingUid))) { log(callingSetting, targetPkgSetting, "BLOCKED"); } @@ -695,7 +879,7 @@ public class AppsFilter { } private boolean shouldFilterApplicationInternal(int callingUid, SettingBase callingSetting, - PackageSetting targetPkgSetting, int userId) { + PackageSetting targetPkgSetting, int targetUserId) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplicationInternal"); try { final boolean featureEnabled = mFeatureConfig.isGloballyEnabled(); @@ -705,12 +889,6 @@ public class AppsFilter { } return false; } - if (callingUid < Process.FIRST_APPLICATION_UID) { - if (DEBUG_LOGGING) { - Slog.d(TAG, "filtering skipped; " + callingUid + " is system"); - } - return false; - } if (callingSetting == null) { Slog.wtf(TAG, "No setting found for non system uid " + callingUid); return true; @@ -719,8 +897,14 @@ public class AppsFilter { final ArraySet<PackageSetting> callingSharedPkgSettings; Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingSetting instanceof"); if (callingSetting instanceof PackageSetting) { - callingPkgSetting = (PackageSetting) callingSetting; - callingSharedPkgSettings = null; + if (((PackageSetting) callingSetting).sharedUser == null) { + callingPkgSetting = (PackageSetting) callingSetting; + callingSharedPkgSettings = null; + } else { + callingPkgSetting = null; + callingSharedPkgSettings = + ((PackageSetting) callingSetting).sharedUser.packages; + } } else { callingPkgSetting = null; callingSharedPkgSettings = ((SharedUserSetting) callingSetting).packages; @@ -778,13 +962,17 @@ public class AppsFilter { } try { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "hasPermission"); - if (callingSetting.getPermissionsState().hasPermission( - Manifest.permission.QUERY_ALL_PACKAGES, UserHandle.getUserId(callingUid))) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "has query-all permission"); + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "requestsQueryAllPackages"); + if (callingPkgSetting != null) { + if (requestsQueryAllPackages(callingPkgSetting)) { + return false; + } + } else { + for (int i = callingSharedPkgSettings.size() - 1; i >= 0; i--) { + if (requestsQueryAllPackages(callingSharedPkgSettings.valueAt(i))) { + return false; + } } - return false; } } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -825,7 +1013,7 @@ public class AppsFilter { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mImplicitlyQueryable"); - final int targetUid = UserHandle.getUid(userId, targetAppId); + final int targetUid = UserHandle.getUid(targetUserId, targetAppId); if (mImplicitlyQueryable.contains(callingUid, targetUid)) { if (DEBUG_LOGGING) { log(callingSetting, targetPkgSetting, "implicitly queryable for user"); @@ -863,13 +1051,20 @@ public class AppsFilter { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - return true; } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } + + private static boolean requestsQueryAllPackages(PackageSetting pkgSetting) { + // we're not guaranteed to have permissions yet analyzed at package add, so we inspect the + // package directly + return pkgSetting.pkg.getRequestedPermissions().contains( + Manifest.permission.QUERY_ALL_PACKAGES); + } + /** Returns {@code true} if the source package instruments the target package. */ private static boolean pkgInstruments(PackageSetting source, PackageSetting target) { try { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 1e3bbd50af3d..34a2abe021ac 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -12359,7 +12359,7 @@ public class PackageManagerService extends IPackageManager.Stub ksms.addScannedPackageLPw(pkg); mComponentResolver.addAllComponents(pkg, chatty); - mAppsFilter.addPackage(pkgSetting, mSettings.mPackages); + mAppsFilter.addPackage(pkgSetting); // Don't allow ephemeral applications to define new permissions groups. if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) { @@ -12533,8 +12533,6 @@ public class PackageManagerService extends IPackageManager.Stub void cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean chatty) { mComponentResolver.removeAllComponents(pkg, chatty); - mAppsFilter.removePackage(getPackageSetting(pkg.getPackageName()), - mInjector.getUserManagerInternal().getUserIds(), mSettings.mPackages); mPermissionManager.removeAllPermissions(pkg, chatty); final int instrumentationSize = ArrayUtils.size(pkg.getInstrumentations()); @@ -14261,7 +14259,7 @@ public class PackageManagerService extends IPackageManager.Stub // Okay! targetPackageSetting.setInstallerPackageName(installerPackageName); mSettings.addInstallerPackageNames(targetPackageSetting.installSource); - mAppsFilter.addPackage(targetPackageSetting, mSettings.mPackages); + mAppsFilter.addPackage(targetPackageSetting); scheduleWriteSettingsLocked(); } } @@ -18686,6 +18684,7 @@ public class PackageManagerService extends IPackageManager.Stub clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL, true); clearDefaultBrowserIfNeeded(packageName); mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName); + mAppsFilter.removePackage(getPackageSetting(packageName)); removedAppId = mSettings.removePackageLPw(packageName); if (outInfo != null) { outInfo.removedAppId = removedAppId; @@ -23443,6 +23442,7 @@ public class PackageManagerService extends IPackageManager.Stub scheduleWritePackageRestrictionsLocked(userId); scheduleWritePackageListLocked(userId); primeDomainVerificationsLPw(userId); + mAppsFilter.onUsersChanged(); } } diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index 51e07faf8443..8000c639139f 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -63,6 +63,10 @@ import libcore.io.IoUtils; import java.io.File; import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Objects; /** @@ -557,6 +561,20 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK = 12; private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK = 13; private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK = 14; + // Filter with IORap + private static final int TRON_COMPILATION_FILTER_ASSUMED_VERIFIED_IORAP = 15; + private static final int TRON_COMPILATION_FILTER_EXTRACT_IORAP = 16; + private static final int TRON_COMPILATION_FILTER_VERIFY_IORAP = 17; + private static final int TRON_COMPILATION_FILTER_QUICKEN_IORAP = 18; + private static final int TRON_COMPILATION_FILTER_SPACE_PROFILE_IORAP = 19; + private static final int TRON_COMPILATION_FILTER_SPACE_IORAP = 20; + private static final int TRON_COMPILATION_FILTER_SPEED_PROFILE_IORAP = 21; + private static final int TRON_COMPILATION_FILTER_SPEED_IORAP = 22; + private static final int TRON_COMPILATION_FILTER_EVERYTHING_PROFILE_IORAP = 23; + private static final int TRON_COMPILATION_FILTER_EVERYTHING_IORAP = 24; + private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_IORAP = 25; + private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK_IORAP = 26; + private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK_IORAP = 27; // Constants used for logging compilation reason to TRON. // DO NOT CHANGE existing values. @@ -623,6 +641,22 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK; case "run-from-vdex-fallback" : return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK; + case "assume-verified-iorap" : return TRON_COMPILATION_FILTER_ASSUMED_VERIFIED_IORAP; + case "extract-iorap" : return TRON_COMPILATION_FILTER_EXTRACT_IORAP; + case "verify-iorap" : return TRON_COMPILATION_FILTER_VERIFY_IORAP; + case "quicken-iorap" : return TRON_COMPILATION_FILTER_QUICKEN_IORAP; + case "space-profile-iorap" : return TRON_COMPILATION_FILTER_SPACE_PROFILE_IORAP; + case "space-iorap" : return TRON_COMPILATION_FILTER_SPACE_IORAP; + case "speed-profile-iorap" : return TRON_COMPILATION_FILTER_SPEED_PROFILE_IORAP; + case "speed-iorap" : return TRON_COMPILATION_FILTER_SPEED_IORAP; + case "everything-profile-iorap" : + return TRON_COMPILATION_FILTER_EVERYTHING_PROFILE_IORAP; + case "everything-iorap" : return TRON_COMPILATION_FILTER_EVERYTHING_IORAP; + case "run-from-apk-iorap" : return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_IORAP; + case "run-from-apk-fallback-iorap" : + return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK_IORAP; + case "run-from-vdex-fallback-iorap" : + return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK_IORAP; default: return TRON_COMPILATION_FILTER_UNKNOWN; } } @@ -640,9 +674,12 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { } private class ArtManagerInternalImpl extends ArtManagerInternal { + private static final String IORAP_DIR = "/data/misc/iorapd"; + private static final String TAG = "ArtManagerInternalImpl"; + @Override public PackageOptimizationInfo getPackageOptimizationInfo( - ApplicationInfo info, String abi) { + ApplicationInfo info, String abi, String activityName) { String compilationReason; String compilationFilter; try { @@ -662,11 +699,45 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { compilationReason = "error"; } + if (checkIorapCompiledTrace(info.packageName, activityName, info.longVersionCode)) { + compilationFilter = compilationFilter + "-iorap"; + } + int compilationFilterTronValue = getCompilationFilterTronValue(compilationFilter); int compilationReasonTronValue = getCompilationReasonTronValue(compilationReason); return new PackageOptimizationInfo( compilationFilterTronValue, compilationReasonTronValue); } + + /* + * Checks the existence of IORap compiled trace for an app. + * + * @return true if the compiled trace exists and the size is greater than 1kb. + */ + private boolean checkIorapCompiledTrace( + String packageName, String activityName, long version) { + // For example: /data/misc/iorapd/com.google.android.GoogleCamera/ + // 60092239/com.android.camera.CameraLauncher/compiled_traces/compiled_trace.pb + Path tracePath = Paths.get(IORAP_DIR, + packageName, + Long.toString(version), + activityName, + "compiled_traces", + "compiled_trace.pb"); + try { + boolean exists = Files.exists(tracePath); + Log.d(TAG, tracePath.toString() + (exists? " exists" : " doesn't exist")); + if (exists) { + long bytes = Files.size(tracePath); + Log.d(TAG, tracePath.toString() + " size is " + Long.toString(bytes)); + return bytes > 0L; + } + return exists; + } catch (IOException e) { + Log.d(TAG, e.getMessage()); + return false; + } + } } } diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 8c75e7a945bc..4f18d07a843f 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -150,7 +150,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba // The list of all rollbacks, including available and committed rollbacks. // Accessed on the handler thread only. - private final List<Rollback> mRollbacks; + private final List<Rollback> mRollbacks = new ArrayList<>(); // Apk sessions from a staged session with no matching rollback. // Accessed on the handler thread only. @@ -189,20 +189,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba mPackageHealthObserver = new RollbackPackageHealthObserver(mContext); mAppDataRollbackHelper = new AppDataRollbackHelper(mInstaller); - // Load rollback data from device storage. - mRollbacks = mRollbackStore.loadRollbacks(); - if (!context.getPackageManager().isDeviceUpgrading()) { - for (Rollback rollback : mRollbacks) { - mAllocatedRollbackIds.put(rollback.info.getRollbackId(), true); - } - } else { - // Delete rollbacks when build fingerprint has changed. - for (Rollback rollback : mRollbacks) { - rollback.delete(mAppDataRollbackHelper); - } - mRollbacks.clear(); - } - // Kick off and start monitoring the handler thread. HandlerThread handlerThread = new HandlerThread("RollbackManagerServiceHandler"); handlerThread.start(); @@ -210,6 +196,22 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba Watchdog.getInstance().addThread(getHandler(), HANDLER_THREAD_TIMEOUT_DURATION_MILLIS); mExecutor = new HandlerExecutor(getHandler()); + // Load rollback data from device storage. + getHandler().post(() -> { + mRollbacks.addAll(mRollbackStore.loadRollbacks()); + if (!context.getPackageManager().isDeviceUpgrading()) { + for (Rollback rollback : mRollbacks) { + mAllocatedRollbackIds.put(rollback.info.getRollbackId(), true); + } + } else { + // Delete rollbacks when build fingerprint has changed. + for (Rollback rollback : mRollbacks) { + rollback.delete(mAppDataRollbackHelper); + } + mRollbacks.clear(); + } + }); + UserManager userManager = mContext.getSystemService(UserManager.class); for (UserHandle user : userManager.getUserHandles(true)) { registerUserCallbacks(user); diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 7fe21e32cbaf..802a35560ba5 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -236,6 +236,17 @@ public class StatsPullAtomService extends SystemService { private static final String DANGEROUS_PERMISSION_STATE_SAMPLE_RATE = "dangerous_permission_state_sample_rate"; + /** Parameters relating to ProcStats data upload. */ + // Maximum shards to use when generating StatsEvent objects from ProcStats. + private static final int MAX_PROCSTATS_SHARDS = 5; + // Should match MAX_PAYLOAD_SIZE in StatsEvent, minus a small amount for overhead/metadata. + private static final int MAX_PROCSTATS_SHARD_SIZE = 48 * 1024; // 48 KB + // In ProcessStats, we measure the size of a raw ProtoOutputStream, before compaction. This + // typically runs 35-45% larger than the compacted size that will be written to StatsEvent. + // Hence, we can allow a little more room in each shard before moving to the next. Make this + // 20% as a conservative estimate. + private static final int MAX_PROCSTATS_RAW_SHARD_SIZE = (int) (MAX_PROCSTATS_SHARD_SIZE * 1.20); + private final Object mThermalLock = new Object(); @GuardedBy("mThermalLock") private IThermalService mThermalService; @@ -2554,19 +2565,26 @@ public class StatsPullAtomService extends SystemService { long lastHighWaterMark = readProcStatsHighWaterMark(section); List<ParcelFileDescriptor> statsFiles = new ArrayList<>(); + ProtoOutputStream[] protoStreams = new ProtoOutputStream[MAX_PROCSTATS_SHARDS]; + for (int i = 0; i < protoStreams.length; i++) { + protoStreams[i] = new ProtoOutputStream(); + } + ProcessStats procStats = new ProcessStats(false); + // Force processStatsService to aggregate all in-storage and in-memory data. long highWaterMark = processStatsService.getCommittedStatsMerged( lastHighWaterMark, section, true, statsFiles, procStats); + procStats.dumpAggregatedProtoForStatsd(protoStreams, MAX_PROCSTATS_RAW_SHARD_SIZE); - // aggregate the data together for westworld consumption - ProtoOutputStream proto = new ProtoOutputStream(); - procStats.dumpAggregatedProtoForStatsd(proto); - - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeByteArray(proto.getBytes()) - .build(); - pulledData.add(e); + for (ProtoOutputStream proto : protoStreams) { + if (proto.getBytes().length > 0) { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeByteArray(proto.getBytes()) + .build(); + pulledData.add(e); + } + } new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + lastHighWaterMark) .delete(); diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index c38d649ada9b..5f6323369d0a 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -115,7 +115,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { private static final String TAG = "UriGrantsManagerService"; // Maximum number of persisted Uri grants a package is allowed private static final int MAX_PERSISTED_URI_GRANTS = 128; - private static final boolean ENABLE_DYNAMIC_PERMISSIONS = false; + private static final boolean ENABLE_DYNAMIC_PERMISSIONS = true; private final Object mLock = new Object(); private final H mH; diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index fb29f9a93215..189b21fb81a6 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -842,7 +842,8 @@ class ActivityMetricsLogger { ? PackageOptimizationInfo.createWithNoInfo() : artManagerInternal.getPackageOptimizationInfo( info.applicationInfo, - info.launchedActivityAppRecordRequiredAbi); + info.launchedActivityAppRecordRequiredAbi, + info.launchedActivityName); builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_REASON, packageOptimizationInfo.getCompilationReason()); builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_FILTER, diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 45b35da7df68..5acedcdad783 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -639,8 +639,14 @@ class ActivityStarter { mRequest.intent, caller); } - // Do not lock the resolving to avoid potential deadlock. + // If the caller hasn't already resolved the activity, we're willing + // to do so here, but because that may require acquiring the AM lock + // as part of calculating the NeededUriGrants, we must never hold + // the WM lock here to avoid deadlocking. if (mRequest.activityInfo == null) { + if (Thread.holdsLock(mService.mGlobalLock)) { + Slog.wtf(TAG, new IllegalStateException("Caller must not hold WM lock")); + } mRequest.resolveActivity(mSupervisor); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 0f63320d54ea..ae562d9dd933 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -6159,12 +6159,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) { assertPackageMatchesCallingUid(callingPackage); - synchronized (mGlobalLock) { - return getActivityStartController().startActivitiesInPackage(uid, realCallingPid, - realCallingUid, callingPackage, callingFeatureId, intents, resolvedTypes, - resultTo, options, userId, validateIncomingUser, originatingPendingIntent, - allowBackgroundActivityStart); - } + return getActivityStartController().startActivitiesInPackage(uid, realCallingPid, + realCallingUid, callingPackage, callingFeatureId, intents, resolvedTypes, + resultTo, options, userId, validateIncomingUser, originatingPendingIntent, + allowBackgroundActivityStart); } @Override @@ -6175,13 +6173,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) { assertPackageMatchesCallingUid(callingPackage); - synchronized (mGlobalLock) { - return getActivityStartController().startActivityInPackage(uid, realCallingPid, - realCallingUid, callingPackage, callingFeatureId, intent, resolvedType, - resultTo, resultWho, requestCode, startFlags, options, userId, inTask, - reason, validateIncomingUser, originatingPendingIntent, - allowBackgroundActivityStart); - } + return getActivityStartController().startActivityInPackage(uid, realCallingPid, + realCallingUid, callingPackage, callingFeatureId, intent, resolvedType, + resultTo, resultWho, requestCode, startFlags, options, userId, inTask, + reason, validateIncomingUser, originatingPendingIntent, + allowBackgroundActivityStart); } @Override diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index fb41a45ec185..794d78889838 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -2159,6 +2159,10 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis return (mDisplay.getFlags() & FLAG_PRIVATE) != 0; } + boolean isTrusted() { + return mDisplay.isTrusted(); + } + /** * Returns the topmost stack on the display that is compatible with the input windowing mode and * activity type. Null is no compatible stack on the display. @@ -3468,7 +3472,7 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis } boolean canShowIme() { - if (isUntrustedVirtualDisplay()) { + if (!isTrusted()) { return false; } return mWmService.mDisplayWindowSettings.shouldShowImeLocked(this) @@ -4565,15 +4569,7 @@ class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.Dis // VR virtual display will be used to run and render 2D app within a VR experience. && mDisplayId != mWmService.mVr2dDisplayId // Do not show system decorations on untrusted virtual display. - && !isUntrustedVirtualDisplay(); - } - - /** - * @return {@code true} if the display is non-system created virtual display. - */ - boolean isUntrustedVirtualDisplay() { - return mDisplay.getType() == Display.TYPE_VIRTUAL - && mDisplay.getOwnerUid() != Process.SYSTEM_UID; + && isTrusted(); } /** diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index be6e4b76e8ed..3d7873ad2f7c 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -384,7 +384,7 @@ class InsetsPolicy { InsetsPolicyAnimationControlCallbacks mControlCallbacks; InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types) { - super(show, false /* hasCallbacks */, types); + super(show, false /* hasCallbacks */, types, false /* disable */); mFinishCallback = finishCallback; mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this); } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index e0568af12944..07e309e1126a 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -353,7 +353,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { } // We don't allow untrusted display to top when task stack moves to top, // until user tapping this display to change display position as top intentionally. - if (mDisplayContent.isUntrustedVirtualDisplay() && !getParent().isOnTop()) { + if (!mDisplayContent.isTrusted() && !getParent().isOnTop()) { includingParents = false; } final int targetPosition = findPositionForStack(position, child, false /* adding */); @@ -1529,8 +1529,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { @Nullable ActivityStack getOrCreateRootHomeTask(boolean onTop) { ActivityStack homeTask = getRootHomeTask(); - if (homeTask == null && mDisplayContent.supportsSystemDecorations() - && !mDisplayContent.isUntrustedVirtualDisplay()) { + if (homeTask == null && mDisplayContent.supportsSystemDecorations()) { homeTask = createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, onTop); } return homeTask; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 7c387d592a2a..27017852ffea 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7163,9 +7163,6 @@ public class WindowManagerService extends IWindowManager.Stub + "not exist: %d", displayId); return false; } - if (displayContent.isUntrustedVirtualDisplay()) { - return false; - } return displayContent.supportsSystemDecorations(); } } @@ -7184,7 +7181,7 @@ public class WindowManagerService extends IWindowManager.Stub + "does not exist: %d", displayId); return; } - if (displayContent.isUntrustedVirtualDisplay()) { + if (!displayContent.isTrusted()) { throw new SecurityException("Attempted to set system decors flag to an " + "untrusted virtual display: " + displayId); } @@ -7232,7 +7229,7 @@ public class WindowManagerService extends IWindowManager.Stub + "exist: %d", displayId); return; } - if (displayContent.isUntrustedVirtualDisplay()) { + if (!displayContent.isTrusted()) { throw new SecurityException("Attempted to set IME flag to an untrusted " + "virtual display: " + displayId); } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 06b22949b6cb..83cd090de674 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2852,7 +2852,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // Do not allow untrusted virtual display to receive keys unless user intentionally // touches the display. return fromUserTouch || getDisplayContent().isOnTop() - || !getDisplayContent().isUntrustedVirtualDisplay(); + || getDisplayContent().isTrusted(); } @Override @@ -5450,6 +5450,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final WindowState imeTarget = getDisplayContent().mInputMethodTarget; boolean inTokenWithAndAboveImeTarget = imeTarget != null && imeTarget != this && imeTarget.mToken == mToken + && mAttrs.type != TYPE_APPLICATION_STARTING && getParent() != null && imeTarget.compareTo(this) <= 0; return inTokenWithAndAboveImeTarget; diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index c0252363a159..8115ac8c6bef 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -247,10 +247,6 @@ class WindowStateAnimator { private final SurfaceControl.Transaction mPostDrawTransaction = new SurfaceControl.Transaction(); - // Used to track whether we have called detach children on the way to invisibility, in which - // case we need to give the client a new Surface if it lays back out to a visible state. - boolean mChildrenDetached = false; - // Set to true after the first frame of the Pinned stack animation // and reset after the last to ensure we only reset mForceScaleUntilResize // once per animation. @@ -425,7 +421,8 @@ class WindowStateAnimator { // transparent to the app. // If the children are detached, we don't want to reparent them to the new surface. // Instead let the children get removed when the old surface is deleted. - if (mSurfaceController != null && mPendingDestroySurface != null && !mChildrenDetached + if (mSurfaceController != null && mPendingDestroySurface != null + && !mPendingDestroySurface.mChildrenDetached && (mWin.mActivityRecord == null || !mWin.mActivityRecord.isRelaunching())) { mPostDrawTransaction.reparentChildren( mPendingDestroySurface.getClientViewRootSurface(), @@ -461,7 +458,6 @@ class WindowStateAnimator { if (mSurfaceController != null) { return mSurfaceController; } - mChildrenDetached = false; if ((mWin.mAttrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0) { windowType = SurfaceControl.WINDOW_TYPE_DONT_SCREENSHOT; @@ -1365,7 +1361,7 @@ class WindowStateAnimator { mPostDrawTransaction.reparent(pendingSurfaceControl, null); // If the children are detached, we don't want to reparent them to the new surface. // Instead let the children get removed when the old surface is deleted. - if (!mChildrenDetached) { + if (!mPendingDestroySurface.mChildrenDetached) { mPostDrawTransaction.reparentChildren( mPendingDestroySurface.getClientViewRootSurface(), mSurfaceController.mSurfaceControl); @@ -1596,7 +1592,6 @@ class WindowStateAnimator { if (mSurfaceController != null) { mSurfaceController.detachChildren(); } - mChildrenDetached = true; // If the children are detached, it means the app is exiting. We don't want to tear the // content down too early, otherwise we could end up with a flicker. By preserving the // current surface, we ensure the content remains on screen until the window is completely diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index 0a7ca5a0cf35..b2bfcdc8a900 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -90,6 +90,9 @@ class WindowSurfaceController { private final SurfaceControl.Transaction mTmpTransaction; + // Used to track whether we have called detach children on the way to invisibility. + boolean mChildrenDetached; + WindowSurfaceController(String name, int w, int h, int format, int flags, WindowStateAnimator animator, int windowType, int ownerUid) { mAnimator = animator; @@ -144,6 +147,7 @@ class WindowSurfaceController { void detachChildren() { ProtoLog.i(WM_SHOW_TRANSACTIONS, "SEVER CHILDREN"); + mChildrenDetached = true; if (mSurfaceControl != null) { mSurfaceControl.detachChildren(); } diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java index ec56e1ebc8e0..b5c9375fcc0d 100644 --- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java +++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java @@ -122,6 +122,7 @@ import com.android.server.testing.shadows.ShadowBackupDataOutput; import com.android.server.testing.shadows.ShadowEventLog; import com.android.server.testing.shadows.ShadowSystemServiceRegistry; +import com.google.common.base.Charsets; import com.google.common.truth.IterableSubject; import org.junit.After; @@ -1910,7 +1911,8 @@ public class KeyValueBackupTaskTest { } @Test - public void testRunTask_whenTransportReturnsError_updatesFilesAndCleansUp() throws Exception { + public void testRunTask_whenTransportReturnsErrorForGenericPackage_updatesFilesAndCleansUp() + throws Exception { TransportMock transportMock = setUpInitializedTransport(mTransport); when(transportMock.transport.performBackup( argThat(packageInfo(PACKAGE_1)), any(), anyInt())) @@ -1926,6 +1928,39 @@ public class KeyValueBackupTaskTest { assertCleansUpFilesAndAgent(mTransport, PACKAGE_1); } + /** + * Checks that TRANSPORT_ERROR during @pm@ backup keeps the state file untouched. + * http://b/144030477 + */ + @Test + public void testRunTask_whenTransportReturnsErrorForPm_updatesFilesAndCleansUp() + throws Exception { + // TODO(tobiast): Refactor this method to share code with + // testRunTask_whenTransportReturnsErrorForGenericPackage_updatesFilesAndCleansUp + // See patchset 7 of http://ag/11762961 + final PackageData packageData = PM_PACKAGE; + TransportMock transportMock = setUpInitializedTransport(mTransport); + when(transportMock.transport.performBackup( + argThat(packageInfo(packageData)), any(), anyInt())) + .thenReturn(BackupTransport.TRANSPORT_ERROR); + + byte[] pmStateBytes = "fake @pm@ state for testing".getBytes(Charsets.UTF_8); + + Path pmStatePath = createPmStateFile(pmStateBytes.clone()); + PackageManagerBackupAgent pmAgent = spy(createPmAgent()); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, packageData); + runTask(task); + verify(pmAgent, never()).onBackup(any(), any(), any()); + + assertThat(Files.readAllBytes(pmStatePath)).isEqualTo(pmStateBytes.clone()); + + boolean existed = deletePmStateFile(); + assertThat(existed).isTrue(); + // unbindAgent() is skipped for @pm@. Comment in KeyValueBackupTask.java: + // "For PM metadata (for which applicationInfo is null) there is no agent-bound state." + assertCleansUpFiles(mTransport, packageData); + } + @Test public void testRunTask_whenTransportGetBackupQuotaThrowsForPm() throws Exception { TransportMock transportMock = setUpInitializedTransport(mTransport); @@ -2707,21 +2742,29 @@ public class KeyValueBackupTaskTest { * </ul> * </ul> */ - private void createPmStateFile() throws IOException { - createPmStateFile(mTransport); + private Path createPmStateFile() throws IOException { + return createPmStateFile("pmState".getBytes()); + } + + private Path createPmStateFile(byte[] bytes) throws IOException { + return createPmStateFile(bytes, mTransport); + } + + private Path createPmStateFile(TransportData transport) throws IOException { + return createPmStateFile("pmState".getBytes(), mTransport); } - /** @see #createPmStateFile() */ - private void createPmStateFile(TransportData transport) throws IOException { - Files.write(getStateFile(transport, PM_PACKAGE), "pmState".getBytes()); + /** @see #createPmStateFile(byte[]) */ + private Path createPmStateFile(byte[] bytes, TransportData transport) throws IOException { + return Files.write(getStateFile(transport, PM_PACKAGE), bytes); } /** * Forces transport initialization and call to {@link * UserBackupManagerService#resetBackupState(File)} */ - private void deletePmStateFile() throws IOException { - Files.deleteIfExists(getStateFile(mTransport, PM_PACKAGE)); + private boolean deletePmStateFile() throws IOException { + return Files.deleteIfExists(getStateFile(mTransport, PM_PACKAGE)); } /** diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java index 8dfedeca79a6..8e17930b949d 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java @@ -23,6 +23,7 @@ import static org.hamcrest.Matchers.empty; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -32,6 +33,7 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageParser; import android.content.pm.Signature; +import android.content.pm.UserInfo; import android.content.pm.parsing.ParsingPackage; import android.content.pm.parsing.component.ParsedActivity; import android.content.pm.parsing.component.ParsedInstrumentation; @@ -39,9 +41,11 @@ import android.content.pm.parsing.component.ParsedIntentInfo; import android.content.pm.parsing.component.ParsedProvider; import android.os.Build; import android.os.Process; +import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.SparseArray; import androidx.annotation.NonNull; @@ -57,26 +61,36 @@ import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; import java.security.cert.CertificateException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.IntFunction; +import java.util.stream.Collectors; @Presubmit @RunWith(JUnit4.class) public class AppsFilterTest { - private static final int DUMMY_CALLING_UID = 10345; - private static final int DUMMY_TARGET_UID = 10556; - private static final int DUMMY_ACTOR_UID = 10656; - private static final int DUMMY_OVERLAY_UID = 10756; - private static final int DUMMY_ACTOR_TWO_UID = 10856; + private static final int DUMMY_CALLING_APPID = 10345; + private static final int DUMMY_TARGET_APPID = 10556; + private static final int DUMMY_ACTOR_APPID = 10656; + private static final int DUMMY_OVERLAY_APPID = 10756; + private static final int SYSTEM_USER = 0; + private static final int SECONDARY_USER = 10; + private static final int[] USER_ARRAY = {SYSTEM_USER, SECONDARY_USER}; + private static final UserInfo[] USER_INFO_LIST = Arrays.stream(USER_ARRAY).mapToObj( + id -> new UserInfo(id, Integer.toString(id), 0)).toArray(UserInfo[]::new); @Mock AppsFilter.FeatureConfig mFeatureConfigMock; + @Mock + AppsFilter.StateProvider mStateProvider; private ArrayMap<String, PackageSetting> mExisting = new ArrayMap<>(); @@ -170,15 +184,24 @@ public class AppsFilterTest { mExisting = new ArrayMap<>(); MockitoAnnotations.initMocks(this); + doAnswer(invocation -> { + ((AppsFilter.StateProvider.CurrentStateCallback) invocation.getArgument(0)) + .currentState(mExisting, USER_INFO_LIST); + return null; + }).when(mStateProvider) + .runWithState(any(AppsFilter.StateProvider.CurrentStateCallback.class)); + when(mFeatureConfigMock.isGloballyEnabled()).thenReturn(true); - when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class))) - .thenReturn(true); + when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class))).thenAnswer( + (Answer<Boolean>) invocation -> + ((AndroidPackage)invocation.getArgument(SYSTEM_USER)).getTargetSdkVersion() + >= Build.VERSION_CODES.R); } @Test public void testSystemReadyPropogates() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); appsFilter.onSystemReady(); verify(mFeatureConfigMock).onSystemReady(); } @@ -186,22 +209,23 @@ public class AppsFilterTest { @Test public void testQueriesAction_FilterMatches() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, - pkg("com.some.package", new IntentFilter("TEST_ACTION")), DUMMY_TARGET_UID); + pkg("com.some.package", new IntentFilter("TEST_ACTION")), DUMMY_TARGET_APPID); PackageSetting calling = simulateAddPackage(appsFilter, - pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_UID); + pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_APPID); - assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test public void testQueriesProtectedAction_FilterDoesNotMatch() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); final Signature frameworkSignature = Mockito.mock(Signature.class); final PackageParser.SigningDetails frameworkSigningDetails = new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1); @@ -211,164 +235,174 @@ public class AppsFilterTest { b -> b.setSigningDetails(frameworkSigningDetails)); appsFilter.onSystemReady(); - final int activityUid = DUMMY_TARGET_UID; + final int activityUid = DUMMY_TARGET_APPID; PackageSetting targetActivity = simulateAddPackage(appsFilter, pkg("com.target.activity", new IntentFilter("TEST_ACTION")), activityUid); - final int receiverUid = DUMMY_TARGET_UID + 1; + final int receiverUid = DUMMY_TARGET_APPID + 1; PackageSetting targetReceiver = simulateAddPackage(appsFilter, pkgWithReceiver("com.target.receiver", new IntentFilter("TEST_ACTION")), receiverUid); - final int callingUid = DUMMY_CALLING_UID; + final int callingUid = DUMMY_CALLING_APPID; PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.calling.action", new Intent("TEST_ACTION")), callingUid); - final int wildcardUid = DUMMY_CALLING_UID + 1; + final int wildcardUid = DUMMY_CALLING_APPID + 1; PackageSetting callingWildCard = simulateAddPackage(appsFilter, pkg("com.calling.wildcard", new Intent("*")), wildcardUid); - assertFalse(appsFilter.shouldFilterApplication(callingUid, calling, targetActivity, 0)); - assertTrue(appsFilter.shouldFilterApplication(callingUid, calling, targetReceiver, 0)); + assertFalse(appsFilter.shouldFilterApplication(callingUid, calling, targetActivity, + SYSTEM_USER)); + assertTrue(appsFilter.shouldFilterApplication(callingUid, calling, targetReceiver, + SYSTEM_USER)); assertFalse(appsFilter.shouldFilterApplication( - wildcardUid, callingWildCard, targetActivity, 0)); + wildcardUid, callingWildCard, targetActivity, SYSTEM_USER)); assertTrue(appsFilter.shouldFilterApplication( - wildcardUid, callingWildCard, targetReceiver, 0)); + wildcardUid, callingWildCard, targetReceiver, SYSTEM_USER)); } @Test public void testQueriesProvider_FilterMatches() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, - pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_UID); + pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_APPID); PackageSetting calling = simulateAddPackage(appsFilter, pkgQueriesProvider("com.some.other.package", "com.some.authority"), - DUMMY_CALLING_UID); + DUMMY_CALLING_APPID); - assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test public void testQueriesDifferentProvider_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, - pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_UID); + pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_APPID); PackageSetting calling = simulateAddPackage(appsFilter, pkgQueriesProvider("com.some.other.package", "com.some.other.authority"), - DUMMY_CALLING_UID); + DUMMY_CALLING_APPID); - assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test public void testQueriesProviderWithSemiColon_FilterMatches() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, pkgWithProvider("com.some.package", "com.some.authority;com.some.other.authority"), - DUMMY_TARGET_UID); + DUMMY_TARGET_APPID); PackageSetting calling = simulateAddPackage(appsFilter, pkgQueriesProvider("com.some.other.package", "com.some.authority"), - DUMMY_CALLING_UID); + DUMMY_CALLING_APPID); - assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test public void testQueriesAction_NoMatchingAction_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, - pkg("com.some.package"), DUMMY_TARGET_UID); + pkg("com.some.package"), DUMMY_TARGET_APPID); PackageSetting calling = simulateAddPackage(appsFilter, - pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_UID); + pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_APPID); - assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, - pkg("com.some.package"), DUMMY_TARGET_UID); - PackageSetting calling = simulateAddPackage(appsFilter, - pkg("com.some.other.package", - new Intent("TEST_ACTION")) - .setTargetSdkVersion(Build.VERSION_CODES.P), - DUMMY_CALLING_UID); + pkg("com.some.package"), DUMMY_TARGET_APPID); + ParsingPackage callingPkg = pkg("com.some.other.package", + new Intent("TEST_ACTION")) + .setTargetSdkVersion(Build.VERSION_CODES.P); + PackageSetting calling = simulateAddPackage(appsFilter, callingPkg, + DUMMY_CALLING_APPID); - when(mFeatureConfigMock.packageIsEnabled(calling.pkg)).thenReturn(false); - assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test public void testNoQueries_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, - pkg("com.some.package"), DUMMY_TARGET_UID); + pkg("com.some.package"), DUMMY_TARGET_APPID); PackageSetting calling = simulateAddPackage(appsFilter, - pkg("com.some.other.package"), DUMMY_CALLING_UID); + pkg("com.some.other.package"), DUMMY_CALLING_APPID); - assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test public void testForceQueryable_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, - pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_UID); + pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_APPID); PackageSetting calling = simulateAddPackage(appsFilter, - pkg("com.some.other.package"), DUMMY_CALLING_UID); + pkg("com.some.other.package"), DUMMY_CALLING_APPID); - assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test public void testForceQueryableByDevice_SystemCaller_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"}, + false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, - pkg("com.some.package"), DUMMY_TARGET_UID, + pkg("com.some.package"), DUMMY_TARGET_APPID, setting -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM)); PackageSetting calling = simulateAddPackage(appsFilter, - pkg("com.some.other.package"), DUMMY_CALLING_UID); + pkg("com.some.other.package"), DUMMY_CALLING_APPID); - assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test public void testSystemSignedTarget_DoesntFilter() throws CertificateException { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); appsFilter.onSystemReady(); final Signature frameworkSignature = Mockito.mock(Signature.class); @@ -382,62 +416,67 @@ public class AppsFilterTest { simulateAddPackage(appsFilter, pkg("android"), 1000, b -> b.setSigningDetails(frameworkSigningDetails)); PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"), - DUMMY_TARGET_UID, + DUMMY_TARGET_APPID, b -> b.setSigningDetails(frameworkSigningDetails) .setPkgFlags(ApplicationInfo.FLAG_SYSTEM)); PackageSetting calling = simulateAddPackage(appsFilter, - pkg("com.some.other.package"), DUMMY_CALLING_UID, + pkg("com.some.other.package"), DUMMY_CALLING_APPID, b -> b.setSigningDetails(otherSigningDetails)); - assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test public void testForceQueryableByDevice_NonSystemCaller_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"}, + false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, - pkg("com.some.package"), DUMMY_TARGET_UID); + pkg("com.some.package"), DUMMY_TARGET_APPID); PackageSetting calling = simulateAddPackage(appsFilter, - pkg("com.some.other.package"), DUMMY_CALLING_UID); + pkg("com.some.other.package"), DUMMY_CALLING_APPID); - assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test public void testSystemQueryable_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, true /* system force queryable */, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, - pkg("com.some.package"), DUMMY_TARGET_UID, + pkg("com.some.package"), DUMMY_TARGET_APPID, setting -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM)); PackageSetting calling = simulateAddPackage(appsFilter, - pkg("com.some.other.package"), DUMMY_CALLING_UID); + pkg("com.some.other.package"), DUMMY_CALLING_APPID); - assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test public void testQueriesPackage_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, - pkg("com.some.package"), DUMMY_TARGET_UID); + pkg("com.some.package"), DUMMY_TARGET_APPID); PackageSetting calling = simulateAddPackage(appsFilter, - pkg("com.some.other.package", "com.some.package"), DUMMY_CALLING_UID); + pkg("com.some.other.package", "com.some.package"), DUMMY_CALLING_APPID); - assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test @@ -445,63 +484,67 @@ public class AppsFilterTest { when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class))) .thenReturn(false); final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage( - appsFilter, pkg("com.some.package"), DUMMY_TARGET_UID); + appsFilter, pkg("com.some.package"), DUMMY_TARGET_APPID); PackageSetting calling = simulateAddPackage( - appsFilter, pkg("com.some.other.package"), DUMMY_CALLING_UID); + appsFilter, pkg("com.some.other.package"), DUMMY_CALLING_APPID); - assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test public void testSystemUid_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, - pkg("com.some.package"), DUMMY_TARGET_UID); + pkg("com.some.package"), DUMMY_TARGET_APPID); - assertFalse(appsFilter.shouldFilterApplication(0, null, target, 0)); + assertFalse(appsFilter.shouldFilterApplication(SYSTEM_USER, null, target, SYSTEM_USER)); assertFalse(appsFilter.shouldFilterApplication(Process.FIRST_APPLICATION_UID - 1, - null, target, 0)); + null, target, SYSTEM_USER)); } @Test public void testNonSystemUid_NoCallingSetting_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, - pkg("com.some.package"), DUMMY_TARGET_UID); + pkg("com.some.package"), DUMMY_TARGET_APPID); - assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, null, target, 0)); + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, null, target, + SYSTEM_USER)); } @Test public void testNoTargetPackage_filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = new PackageSettingBuilder() + .setAppId(DUMMY_TARGET_APPID) .setName("com.some.package") .setCodePath("/") .setResourcePath("/") .setPVersionCode(1L) .build(); PackageSetting calling = simulateAddPackage(appsFilter, - pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_UID); + pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_APPID); - assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test @@ -516,7 +559,11 @@ public class AppsFilterTest { .setOverlayTargetName("overlayableName"); ParsingPackage actor = pkg("com.some.package.actor"); - final AppsFilter appsFilter = new AppsFilter(mFeatureConfigMock, new String[]{}, false, + final AppsFilter appsFilter = new AppsFilter( + mStateProvider, + mFeatureConfigMock, + new String[]{}, + false, new OverlayReferenceMapper.Provider() { @Nullable @Override @@ -544,31 +591,34 @@ public class AppsFilterTest { simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); - PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_UID); - PackageSetting overlaySetting = simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_UID); - PackageSetting actorSetting = simulateAddPackage(appsFilter, actor, DUMMY_ACTOR_UID); + PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_APPID); + PackageSetting overlaySetting = + simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_APPID); + PackageSetting actorSetting = simulateAddPackage(appsFilter, actor, DUMMY_ACTOR_APPID); // Actor can see both target and overlay - assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_UID, actorSetting, - targetSetting, 0)); - assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_UID, actorSetting, - overlaySetting, 0)); + assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSetting, + targetSetting, SYSTEM_USER)); + assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSetting, + overlaySetting, SYSTEM_USER)); // But target/overlay can't see each other - assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, targetSetting, - overlaySetting, 0)); - assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_UID, overlaySetting, - targetSetting, 0)); + assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_APPID, targetSetting, + overlaySetting, SYSTEM_USER)); + assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_APPID, overlaySetting, + targetSetting, SYSTEM_USER)); // And can't see the actor - assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, targetSetting, - actorSetting, 0)); - assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_UID, overlaySetting, - actorSetting, 0)); + assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_APPID, targetSetting, + actorSetting, SYSTEM_USER)); + assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_APPID, overlaySetting, + actorSetting, SYSTEM_USER)); } @Test public void testActsOnTargetOfOverlayThroughSharedUser() throws Exception { +// Debug.waitForDebugger(); + final String actorName = "overlay://test/actorName"; ParsingPackage target = pkg("com.some.package.target") @@ -580,7 +630,11 @@ public class AppsFilterTest { ParsingPackage actorOne = pkg("com.some.package.actor.one"); ParsingPackage actorTwo = pkg("com.some.package.actor.two"); - final AppsFilter appsFilter = new AppsFilter(mFeatureConfigMock, new String[]{}, false, + final AppsFilter appsFilter = new AppsFilter( + mStateProvider, + mFeatureConfigMock, + new String[]{}, + false, new OverlayReferenceMapper.Provider() { @Nullable @Override @@ -609,108 +663,114 @@ public class AppsFilterTest { simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); - PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_UID); - PackageSetting overlaySetting = simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_UID); - PackageSetting actorOneSetting = simulateAddPackage(appsFilter, actorOne, DUMMY_ACTOR_UID); - PackageSetting actorTwoSetting = simulateAddPackage(appsFilter, actorTwo, - DUMMY_ACTOR_TWO_UID); - + PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_APPID); SharedUserSetting actorSharedSetting = new SharedUserSetting("actorSharedUser", - actorOneSetting.pkgFlags, actorOneSetting.pkgPrivateFlags); - actorSharedSetting.addPackage(actorOneSetting); - actorSharedSetting.addPackage(actorTwoSetting); + targetSetting.pkgFlags, targetSetting.pkgPrivateFlags); + PackageSetting overlaySetting = + simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_APPID); + simulateAddPackage(appsFilter, actorOne, DUMMY_ACTOR_APPID, + null /*settingBuilder*/, actorSharedSetting); + simulateAddPackage(appsFilter, actorTwo, DUMMY_ACTOR_APPID, + null /*settingBuilder*/, actorSharedSetting); + // actorTwo can see both target and overlay - assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_TWO_UID, actorSharedSetting, - targetSetting, 0)); - assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_TWO_UID, actorSharedSetting, - overlaySetting, 0)); + assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSharedSetting, + targetSetting, SYSTEM_USER)); + assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSharedSetting, + overlaySetting, SYSTEM_USER)); } @Test public void testInitiatingApp_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"), - DUMMY_TARGET_UID); + DUMMY_TARGET_APPID); PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"), - DUMMY_CALLING_UID, withInstallSource(target.name, null, null, null, false)); + DUMMY_CALLING_APPID, withInstallSource(target.name, null, null, null, false)); - assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test public void testUninstalledInitiatingApp_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"), - DUMMY_TARGET_UID); + DUMMY_TARGET_APPID); PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"), - DUMMY_CALLING_UID, withInstallSource(target.name, null, null, null, true)); + DUMMY_CALLING_APPID, withInstallSource(target.name, null, null, null, true)); - assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test public void testOriginatingApp_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"), - DUMMY_TARGET_UID); + DUMMY_TARGET_APPID); PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"), - DUMMY_CALLING_UID, withInstallSource(null, target.name, null, null, false)); + DUMMY_CALLING_APPID, withInstallSource(null, target.name, null, null, false)); - assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test public void testInstallingApp_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"), - DUMMY_TARGET_UID); + DUMMY_TARGET_APPID); PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"), - DUMMY_CALLING_UID, withInstallSource(null, null, target.name, null, false)); + DUMMY_CALLING_APPID, withInstallSource(null, null, target.name, null, false)); - assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); } @Test public void testInstrumentation_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"), - DUMMY_TARGET_UID); + DUMMY_TARGET_APPID); PackageSetting instrumentation = simulateAddPackage(appsFilter, pkgWithInstrumentation("com.some.other.package", "com.some.package"), - DUMMY_CALLING_UID); + DUMMY_CALLING_APPID); assertFalse( - appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, instrumentation, target, 0)); + appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, instrumentation, target, + SYSTEM_USER)); assertFalse( - appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, target, instrumentation, 0)); + appsFilter.shouldFilterApplication(DUMMY_TARGET_APPID, target, instrumentation, + SYSTEM_USER)); } @Test public void testWhoCanSee() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -718,6 +778,7 @@ public class AppsFilterTest { final int seesNothingAppId = Process.FIRST_APPLICATION_UID; final int hasProviderAppId = Process.FIRST_APPLICATION_UID + 1; final int queriesProviderAppId = Process.FIRST_APPLICATION_UID + 2; + PackageSetting system = simulateAddPackage(appsFilter, pkg("some.system.pkg"), systemAppId); PackageSetting seesNothing = simulateAddPackage(appsFilter, pkg("com.some.package"), seesNothingAppId); @@ -727,23 +788,25 @@ public class AppsFilterTest { pkgQueriesProvider("com.yet.some.other.package", "com.some.authority"), queriesProviderAppId); - final int[] systemFilter = - appsFilter.getVisibilityWhitelist(system, new int[]{0}, mExisting).get(0); - assertThat(toList(systemFilter), empty()); + final SparseArray<int[]> systemFilter = + appsFilter.getVisibilityWhitelist(system, USER_ARRAY, mExisting); + assertThat(toList(systemFilter.get(SYSTEM_USER)), empty()); - final int[] seesNothingFilter = - appsFilter.getVisibilityWhitelist(seesNothing, new int[]{0}, mExisting).get(0); - assertThat(toList(seesNothingFilter), + final SparseArray<int[]> seesNothingFilter = + appsFilter.getVisibilityWhitelist(seesNothing, USER_ARRAY, mExisting); + assertThat(toList(seesNothingFilter.get(SYSTEM_USER)), + contains(seesNothingAppId)); + assertThat(toList(seesNothingFilter.get(SECONDARY_USER)), contains(seesNothingAppId)); - final int[] hasProviderFilter = - appsFilter.getVisibilityWhitelist(hasProvider, new int[]{0}, mExisting).get(0); - assertThat(toList(hasProviderFilter), + final SparseArray<int[]> hasProviderFilter = + appsFilter.getVisibilityWhitelist(hasProvider, USER_ARRAY, mExisting); + assertThat(toList(hasProviderFilter.get(SYSTEM_USER)), contains(hasProviderAppId, queriesProviderAppId)); - int[] queriesProviderFilter = - appsFilter.getVisibilityWhitelist(queriesProvider, new int[]{0}, mExisting).get(0); - assertThat(toList(queriesProviderFilter), + SparseArray<int[]> queriesProviderFilter = + appsFilter.getVisibilityWhitelist(queriesProvider, USER_ARRAY, mExisting); + assertThat(toList(queriesProviderFilter.get(SYSTEM_USER)), contains(queriesProviderAppId)); // provider read @@ -751,8 +814,8 @@ public class AppsFilterTest { // ensure implicit access is included in the filter queriesProviderFilter = - appsFilter.getVisibilityWhitelist(queriesProvider, new int[]{0}, mExisting).get(0); - assertThat(toList(queriesProviderFilter), + appsFilter.getVisibilityWhitelist(queriesProvider, USER_ARRAY, mExisting); + assertThat(toList(queriesProviderFilter.get(SYSTEM_USER)), contains(hasProviderAppId, queriesProviderAppId)); } @@ -779,11 +842,17 @@ public class AppsFilterTest { private PackageSetting simulateAddPackage(AppsFilter filter, ParsingPackage newPkgBuilder, int appId) { - return simulateAddPackage(filter, newPkgBuilder, appId, null); + return simulateAddPackage(filter, newPkgBuilder, appId, null /*settingBuilder*/); } private PackageSetting simulateAddPackage(AppsFilter filter, ParsingPackage newPkgBuilder, int appId, @Nullable WithSettingBuilder action) { + return simulateAddPackage(filter, newPkgBuilder, appId, action, null /*sharedUserSetting*/); + } + + private PackageSetting simulateAddPackage(AppsFilter filter, + ParsingPackage newPkgBuilder, int appId, @Nullable WithSettingBuilder action, + @Nullable SharedUserSetting sharedUserSetting) { AndroidPackage newPkg = ((ParsedPackage) newPkgBuilder.hideAsParsed()).hideAsFinal(); final PackageSettingBuilder settingBuilder = new PackageSettingBuilder() @@ -795,8 +864,12 @@ public class AppsFilterTest { .setPVersionCode(1L); final PackageSetting setting = (action == null ? settingBuilder : action.withBuilder(settingBuilder)).build(); - filter.addPackage(setting, mExisting); mExisting.put(newPkg.getPackageName(), setting); + if (sharedUserSetting != null) { + sharedUserSetting.addPackage(setting); + setting.sharedUser = sharedUserSetting; + } + filter.addPackage(setting); return setting; } @@ -809,4 +882,3 @@ public class AppsFilterTest { return setting -> setting.setInstallSource(installSource); } } - diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index d3f677ced329..5a952b3e238f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1287,7 +1287,6 @@ public class DisplayContentTests extends WindowTestsBase { public void testGetOrCreateRootHomeTask_supportedSecondaryDisplay() { DisplayContent display = createNewDisplay(); doReturn(true).when(display).supportsSystemDecorations(); - doReturn(false).when(display).isUntrustedVirtualDisplay(); // Remove the current home stack if it exists so a new one can be created below. TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); @@ -1311,10 +1310,10 @@ public class DisplayContentTests extends WindowTestsBase { } @Test - public void testGetOrCreateRootHomeTask_untrustedVirtualDisplay() { + public void testGetOrCreateRootHomeTask_untrustedDisplay() { DisplayContent display = createNewDisplay(); TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); - doReturn(true).when(display).isUntrustedVirtualDisplay(); + doReturn(false).when(display).isTrusted(); assertNull(taskDisplayArea.getRootHomeTask()); assertNull(taskDisplayArea.getOrCreateRootHomeTask()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index 512042cdf7b9..786f8d8af024 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -150,9 +150,9 @@ public class TaskDisplayAreaTests extends WindowTestsBase { @Test public void testDisplayPositionWithPinnedStack() { - // Make sure the display is system owned display which capable to move the stack to top. + // Make sure the display is trusted display which capable to move the stack to top. spyOn(mDisplayContent); - doReturn(false).when(mDisplayContent).isUntrustedVirtualDisplay(); + doReturn(true).when(mDisplayContent).isTrusted(); // The display contains pinned stack that was added in {@link #setUp}. final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index e9ed20bd9683..b51784d8d6e5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -692,4 +692,14 @@ public class WindowStateTests extends WindowTestsBase { sameTokenWindow.removeImmediately(); assertFalse(sameTokenWindow.needsRelativeLayeringToIme()); } + + @Test + public void testNeedsRelativeLayeringToIme_startingWindow() { + WindowState sameTokenWindow = createWindow(null, TYPE_APPLICATION_STARTING, + mAppWindow.mToken, "SameTokenWindow"); + mDisplayContent.mInputMethodTarget = mAppWindow; + sameTokenWindow.mActivityRecord.getStack().setWindowingMode( + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + assertFalse(sameTokenWindow.needsRelativeLayeringToIme()); + } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java index f4dea5bcf3e4..9516af624f6f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java @@ -54,6 +54,8 @@ import org.junit.runners.MethodSorters; @LargeTest @RunWith(AndroidJUnit4.class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest(bugId = 159096424) +@Ignore("Waiting bug feedback") public class ResizeSplitScreenTest extends FlickerTestBase { private static String sSimpleActivity = "SimpleActivity"; diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index d53baf2faa27..1e7fecf50d85 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -100,6 +100,7 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.startsWith; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.any; @@ -164,6 +165,8 @@ import android.net.LinkAddress; import android.net.LinkProperties; import android.net.MatchAllNetworkSpecifier; import android.net.Network; +import android.net.NetworkAgent; +import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkFactory; import android.net.NetworkInfo; @@ -6808,6 +6811,30 @@ public class ConnectivityServiceTest { assertEquals(wifiLp, mService.getActiveLinkProperties()); } + @Test + public void testLegacyExtraInfoSentToNetworkMonitor() throws Exception { + class TestNetworkAgent extends NetworkAgent { + TestNetworkAgent(Context context, Looper looper, NetworkAgentConfig config) { + super(context, looper, "MockAgent", new NetworkCapabilities(), + new LinkProperties(), 40 , config, null /* provider */); + } + } + final NetworkAgent naNoExtraInfo = new TestNetworkAgent( + mServiceContext, mCsHandlerThread.getLooper(), new NetworkAgentConfig()); + naNoExtraInfo.register(); + verify(mNetworkStack).makeNetworkMonitor(any(), isNull(String.class), any()); + naNoExtraInfo.unregister(); + + reset(mNetworkStack); + final NetworkAgentConfig config = + new NetworkAgentConfig.Builder().setLegacyExtraInfo("legacyinfo").build(); + final NetworkAgent naExtraInfo = new TestNetworkAgent( + mServiceContext, mCsHandlerThread.getLooper(), config); + naExtraInfo.register(); + verify(mNetworkStack).makeNetworkMonitor(any(), eq("legacyinfo"), any()); + naExtraInfo.unregister(); + } + private void setupLocationPermissions( int targetSdk, boolean locationToggle, String op, String perm) throws Exception { final ApplicationInfo applicationInfo = new ApplicationInfo(); diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt index e96365b7af60..985ff1df151f 100644 --- a/wifi/api/system-current.txt +++ b/wifi/api/system-current.txt @@ -253,6 +253,7 @@ package android.net.wifi { method public int getBand(); method @NonNull public java.util.List<android.net.MacAddress> getBlockedClientList(); method public int getChannel(); + method public int getMacRandomizationSetting(); method public int getMaxNumberOfClients(); method public long getShutdownTimeoutMillis(); method public boolean isAutoShutdownEnabled(); @@ -262,6 +263,8 @@ package android.net.wifi { field public static final int BAND_5GHZ = 2; // 0x2 field public static final int BAND_6GHZ = 4; // 0x4 field public static final int BAND_ANY = 7; // 0x7 + field public static final int RANDOMIZATION_NONE = 0; // 0x0 + field public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1 } public static final class SoftApConfiguration.Builder { @@ -276,6 +279,7 @@ package android.net.wifi { method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int, int); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setClientControlByUserEnabled(boolean); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMacRandomizationSetting(int); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(@IntRange(from=0) int); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setPassphrase(@Nullable String, int); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(@IntRange(from=0) long); @@ -285,6 +289,7 @@ package android.net.wifi { public final class SoftApInfo implements android.os.Parcelable { method public int describeContents(); method public int getBandwidth(); + method @Nullable public android.net.MacAddress getBssid(); method public int getFrequency(); method public void writeToParcel(@NonNull android.os.Parcel, int); field public static final int CHANNEL_WIDTH_160MHZ = 6; // 0x6 diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java index 2bcd4f4241a6..68c35d3061b0 100644 --- a/wifi/java/android/net/wifi/SoftApConfiguration.java +++ b/wifi/java/android/net/wifi/SoftApConfiguration.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.net.MacAddress; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -217,6 +218,34 @@ public final class SoftApConfiguration implements Parcelable { */ private final long mShutdownTimeoutMillis; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"RANDOMIZATION_"}, value = { + RANDOMIZATION_NONE, + RANDOMIZATION_PERSISTENT}) + public @interface MacRandomizationSetting {} + + /** + * Use factory MAC as BSSID for the AP + * @hide + */ + @SystemApi + public static final int RANDOMIZATION_NONE = 0; + /** + * Generate a randomized MAC as BSSID for the AP + * @hide + */ + @SystemApi + public static final int RANDOMIZATION_PERSISTENT = 1; + + /** + * Level of MAC randomization for the AP BSSID. + * @hide + */ + @MacRandomizationSetting + private int mMacRandomizationSetting; + + /** * THe definition of security type OPEN. */ @@ -252,7 +281,8 @@ public final class SoftApConfiguration implements Parcelable { @Nullable String passphrase, boolean hiddenSsid, @BandType int band, int channel, @SecurityType int securityType, int maxNumberOfClients, boolean shutdownTimeoutEnabled, long shutdownTimeoutMillis, boolean clientControlByUser, - @NonNull List<MacAddress> blockedList, @NonNull List<MacAddress> allowedList) { + @NonNull List<MacAddress> blockedList, @NonNull List<MacAddress> allowedList, + int macRandomizationSetting) { mSsid = ssid; mBssid = bssid; mPassphrase = passphrase; @@ -266,6 +296,7 @@ public final class SoftApConfiguration implements Parcelable { mClientControlByUser = clientControlByUser; mBlockedClientList = new ArrayList<>(blockedList); mAllowedClientList = new ArrayList<>(allowedList); + mMacRandomizationSetting = macRandomizationSetting; } @Override @@ -289,7 +320,8 @@ public final class SoftApConfiguration implements Parcelable { && mShutdownTimeoutMillis == other.mShutdownTimeoutMillis && mClientControlByUser == other.mClientControlByUser && Objects.equals(mBlockedClientList, other.mBlockedClientList) - && Objects.equals(mAllowedClientList, other.mAllowedClientList); + && Objects.equals(mAllowedClientList, other.mAllowedClientList) + && mMacRandomizationSetting == other.mMacRandomizationSetting; } @Override @@ -297,7 +329,7 @@ public final class SoftApConfiguration implements Parcelable { return Objects.hash(mSsid, mBssid, mPassphrase, mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients, mAutoShutdownEnabled, mShutdownTimeoutMillis, mClientControlByUser, mBlockedClientList, - mAllowedClientList); + mAllowedClientList, mMacRandomizationSetting); } @Override @@ -317,6 +349,7 @@ public final class SoftApConfiguration implements Parcelable { sbuf.append(" \n ClientControlByUser=").append(mClientControlByUser); sbuf.append(" \n BlockedClientList=").append(mBlockedClientList); sbuf.append(" \n AllowedClientList=").append(mAllowedClientList); + sbuf.append(" \n MacRandomizationSetting=").append(mMacRandomizationSetting); return sbuf.toString(); } @@ -335,6 +368,7 @@ public final class SoftApConfiguration implements Parcelable { dest.writeBoolean(mClientControlByUser); dest.writeTypedList(mBlockedClientList); dest.writeTypedList(mAllowedClientList); + dest.writeInt(mMacRandomizationSetting); } @Override @@ -352,7 +386,7 @@ public final class SoftApConfiguration implements Parcelable { in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readBoolean(), in.readLong(), in.readBoolean(), in.createTypedArrayList(MacAddress.CREATOR), - in.createTypedArrayList(MacAddress.CREATOR)); + in.createTypedArrayList(MacAddress.CREATOR), in.readInt()); } @Override @@ -509,6 +543,21 @@ public final class SoftApConfiguration implements Parcelable { } /** + * Returns the level of MAC randomization for the AP BSSID. + * {@link Builder#setMacRandomizationSetting(int)}. + * + * @hide + */ + @SystemApi + @MacRandomizationSetting + public int getMacRandomizationSetting() { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) { + throw new UnsupportedOperationException(); + } + return mMacRandomizationSetting; + } + + /** * Returns a {@link WifiConfiguration} representation of this {@link SoftApConfiguration}. * Note that SoftApConfiguration may contain configuration which is cannot be represented * by the legacy WifiConfiguration, in such cases a null will be returned. @@ -584,6 +633,7 @@ public final class SoftApConfiguration implements Parcelable { private boolean mClientControlByUser; private List<MacAddress> mBlockedClientList; private List<MacAddress> mAllowedClientList; + private int mMacRandomizationSetting; /** * Constructs a Builder with default values (see {@link Builder}). @@ -602,6 +652,7 @@ public final class SoftApConfiguration implements Parcelable { mClientControlByUser = false; mBlockedClientList = new ArrayList<>(); mAllowedClientList = new ArrayList<>(); + mMacRandomizationSetting = RANDOMIZATION_PERSISTENT; } /** @@ -623,6 +674,7 @@ public final class SoftApConfiguration implements Parcelable { mClientControlByUser = other.mClientControlByUser; mBlockedClientList = new ArrayList<>(other.mBlockedClientList); mAllowedClientList = new ArrayList<>(other.mAllowedClientList); + mMacRandomizationSetting = other.mMacRandomizationSetting; } /** @@ -640,7 +692,7 @@ public final class SoftApConfiguration implements Parcelable { return new SoftApConfiguration(mSsid, mBssid, mPassphrase, mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients, mAutoShutdownEnabled, mShutdownTimeoutMillis, mClientControlByUser, - mBlockedClientList, mAllowedClientList); + mBlockedClientList, mAllowedClientList, mMacRandomizationSetting); } /** @@ -969,5 +1021,30 @@ public final class SoftApConfiguration implements Parcelable { mBlockedClientList = new ArrayList<>(blockedClientList); return this; } + + /** + * Specifies the level of MAC randomization for the AP BSSID. + * The Soft AP BSSID will be randomized only if the BSSID isn't set + * {@link #setBssid(MacAddress)} and this method is either uncalled + * or called with {@link #RANDOMIZATION_PERSISTENT}. + * + * <p> + * <li>If not set, defaults to {@link #RANDOMIZATION_PERSISTENT}</li> + * + * @param macRandomizationSetting One of the following setting:. + * {@link #RANDOMIZATION_NONE} or {@link #RANDOMIZATION_PERSISTENT}. + * @return Builder for chaining. + * + * @see #setBssid(MacAddress) + */ + @NonNull + public Builder setMacRandomizationSetting( + @MacRandomizationSetting int macRandomizationSetting) { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) { + throw new UnsupportedOperationException(); + } + mMacRandomizationSetting = macRandomizationSetting; + return this; + } } } diff --git a/wifi/java/android/net/wifi/SoftApInfo.java b/wifi/java/android/net/wifi/SoftApInfo.java index 24ed8effe471..4791275cdce5 100644 --- a/wifi/java/android/net/wifi/SoftApInfo.java +++ b/wifi/java/android/net/wifi/SoftApInfo.java @@ -19,9 +19,13 @@ package android.net.wifi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.net.MacAddress; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.util.Preconditions; + import java.util.Objects; /** @@ -90,6 +94,10 @@ public final class SoftApInfo implements Parcelable { @WifiAnnotations.Bandwidth private int mBandwidth = CHANNEL_WIDTH_INVALID; + /** The MAC Address which AP resides on. */ + @Nullable + private MacAddress mBssid; + /** * Get the frequency which AP resides on. */ @@ -126,12 +134,42 @@ public final class SoftApInfo implements Parcelable { } /** + * Get the MAC address (BSSID) of the AP. Null when AP disabled. + */ + @Nullable + public MacAddress getBssid() { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) { + throw new UnsupportedOperationException(); + } + return mBssid; + } + + /** + * Set the MAC address which AP resides on. + * <p> + * <li>If not set, defaults to null.</li> + * @param bssid BSSID, The caller is responsible for avoiding collisions. + * @throws IllegalArgumentException when the given BSSID is the all-zero or broadcast MAC + * address. + * + * @hide + */ + public void setBssid(@Nullable MacAddress bssid) { + if (bssid != null) { + Preconditions.checkArgument(!bssid.equals(WifiManager.ALL_ZEROS_MAC_ADDRESS)); + Preconditions.checkArgument(!bssid.equals(MacAddress.BROADCAST_ADDRESS)); + } + mBssid = bssid; + } + + /** * @hide */ public SoftApInfo(@Nullable SoftApInfo source) { if (source != null) { mFrequency = source.mFrequency; mBandwidth = source.mBandwidth; + mBssid = source.mBssid; } } @@ -152,6 +190,7 @@ public final class SoftApInfo implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mFrequency); dest.writeInt(mBandwidth); + dest.writeParcelable(mBssid, flags); } @NonNull @@ -161,6 +200,7 @@ public final class SoftApInfo implements Parcelable { SoftApInfo info = new SoftApInfo(); info.mFrequency = in.readInt(); info.mBandwidth = in.readInt(); + info.mBssid = in.readParcelable(MacAddress.class.getClassLoader()); return info; } @@ -172,10 +212,13 @@ public final class SoftApInfo implements Parcelable { @NonNull @Override public String toString() { - return "SoftApInfo{" - + "bandwidth= " + mBandwidth - + ",frequency= " + mFrequency - + '}'; + StringBuilder sbuf = new StringBuilder(); + sbuf.append("SoftApInfo{"); + sbuf.append("bandwidth= ").append(mBandwidth); + sbuf.append(",frequency= ").append(mFrequency); + if (mBssid != null) sbuf.append(",bssid=").append(mBssid.toString()); + sbuf.append("}"); + return sbuf.toString(); } @Override @@ -184,11 +227,12 @@ public final class SoftApInfo implements Parcelable { if (!(o instanceof SoftApInfo)) return false; SoftApInfo softApInfo = (SoftApInfo) o; return mFrequency == softApInfo.mFrequency - && mBandwidth == softApInfo.mBandwidth; + && mBandwidth == softApInfo.mBandwidth + && Objects.equals(mBssid, softApInfo.mBssid); } @Override public int hashCode() { - return Objects.hash(mFrequency, mBandwidth); + return Objects.hash(mFrequency, mBandwidth, mBssid); } } diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java index 1a4427034756..40f5c3ed8e5e 100644 --- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertNull; import android.net.MacAddress; +import android.os.Build; import android.os.Parcel; import androidx.test.filters.SmallTest; @@ -78,6 +79,10 @@ public class SoftApConfigurationTest { assertThat(original.getChannel()).isEqualTo(0); assertThat(original.isHiddenSsid()).isEqualTo(false); assertThat(original.getMaxNumberOfClients()).isEqualTo(0); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) { + assertThat(original.getMacRandomizationSetting()) + .isEqualTo(SoftApConfiguration.RANDOMIZATION_PERSISTENT); + } SoftApConfiguration unparceled = parcelUnparcel(original); assertThat(unparceled).isNotSameAs(original); @@ -120,7 +125,7 @@ public class SoftApConfigurationTest { List<MacAddress> testAllowedClientList = new ArrayList<>(); testBlockedClientList.add(MacAddress.fromString("11:22:33:44:55:66")); testAllowedClientList.add(MacAddress.fromString("aa:bb:cc:dd:ee:ff")); - SoftApConfiguration original = new SoftApConfiguration.Builder() + SoftApConfiguration.Builder originalBuilder = new SoftApConfiguration.Builder() .setPassphrase("secretsecret", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) .setChannel(149, SoftApConfiguration.BAND_5GHZ) .setHiddenSsid(true) @@ -129,8 +134,11 @@ public class SoftApConfigurationTest { .setShutdownTimeoutMillis(500000) .setClientControlByUserEnabled(true) .setBlockedClientList(testBlockedClientList) - .setAllowedClientList(testAllowedClientList) - .build(); + .setAllowedClientList(testAllowedClientList); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) { + originalBuilder.setMacRandomizationSetting(SoftApConfiguration.RANDOMIZATION_NONE); + } + SoftApConfiguration original = originalBuilder.build(); assertThat(original.getPassphrase()).isEqualTo("secretsecret"); assertThat(original.getSecurityType()).isEqualTo( SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); @@ -143,6 +151,10 @@ public class SoftApConfigurationTest { assertThat(original.isClientControlByUserEnabled()).isEqualTo(true); assertThat(original.getBlockedClientList()).isEqualTo(testBlockedClientList); assertThat(original.getAllowedClientList()).isEqualTo(testAllowedClientList); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) { + assertThat(original.getMacRandomizationSetting()) + .isEqualTo(SoftApConfiguration.RANDOMIZATION_NONE); + } SoftApConfiguration unparceled = parcelUnparcel(original); assertThat(unparceled).isNotSameAs(original); diff --git a/wifi/tests/src/android/net/wifi/SoftApInfoTest.java b/wifi/tests/src/android/net/wifi/SoftApInfoTest.java index 929f3ab88fd8..458a95494438 100644 --- a/wifi/tests/src/android/net/wifi/SoftApInfoTest.java +++ b/wifi/tests/src/android/net/wifi/SoftApInfoTest.java @@ -16,6 +16,7 @@ package android.net.wifi; +import android.net.MacAddress; import android.os.Parcel; import static org.junit.Assert.assertEquals; @@ -38,6 +39,8 @@ public class SoftApInfoTest { SoftApInfo info = new SoftApInfo(); info.setFrequency(2412); info.setBandwidth(SoftApInfo.CHANNEL_WIDTH_20MHZ); + info.setBssid(MacAddress.fromString("aa:bb:cc:dd:ee:ff")); + SoftApInfo copiedInfo = new SoftApInfo(info); @@ -53,6 +56,7 @@ public class SoftApInfoTest { SoftApInfo info = new SoftApInfo(); info.setFrequency(2412); info.setBandwidth(SoftApInfo.CHANNEL_WIDTH_20MHZ); + info.setBssid(MacAddress.fromString("aa:bb:cc:dd:ee:ff")); Parcel parcelW = Parcel.obtain(); info.writeToParcel(parcelW, 0); |